trend.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <template>
  2. <u-modal :show="trendModal" title="价格趋势" @confirm="closeTrendModal" confirmText="关闭弹窗" height="93%"><view class="trend_modal">
  3. <RemoteSearchSelect ref="searchSelect" :value="model" placeholder="请输入关键词" :resultsShow="resultsShow" :currentUrl="currentImg" :searchResults="searchResults" :imgResults="imgResults" :loading="loading"
  4. :cameraEnabled="true" @search="handleSearch" @select="handleSelect" @select-img="handleSelectImg" @select-crop-img="handleSelectCropImg" @load-more="loadMore" @load-more-img="loadMoreImg" @clear="handleClear" @focus="handleFocus" @upload="handleSelectCropImg"></RemoteSearchSelect>
  5. <view class="charts_box" v-if="chartShow">
  6. <u--text type="primary" :text="`最大价格:${Math.max(...maxPrice)}元`"></u--text>
  7. <u--text type="warning" :text="`最小价格:${Math.min(...minPrice)}元`"></u--text>
  8. <qiun-data-charts type="line" :chartData="chartData" canvasId="trendChart" :opts="opts" :ontouch="true"
  9. tooltipFormat="tooltipFormatPrice" width="700rpx" height="600rpx" backgroundColor="#ffffff"
  10. @getIndex="handleChartClick" />
  11. </view>
  12. <!-- <u--text v-if="cardData.length > 0" :text="'日期:' + date"></u--text> -->
  13. <view class="card-container" v-if="cardData.length > 0">
  14. <view class="card-item" v-for="(item, index) in cardData" :key="index">
  15. <view class="card-row">
  16. <span class="card-label">型号:</span>
  17. <span class="card-value">{{ item.model }}</span>
  18. </view>
  19. <view class="card-row">
  20. <span class="card-label">日期:</span>
  21. <span class="card-value">{{ item.recycleTime }}</span>
  22. </view>
  23. <view class="card-row">
  24. <span class="card-label">价格:</span>
  25. <span class="card-value">{{ item.price }}元</span>
  26. </view>
  27. <view class="card-row">
  28. <span class="card-label">回收情况:</span>
  29. <span class="card-value">{{ item.recycleSituation }}</span>
  30. </view>
  31. <view class="card-row">
  32. <span class="card-label">备注:</span>
  33. <span class="card-value">{{ item.remark || '-' }}</span>
  34. </view>
  35. </view>
  36. </view>
  37. </view>
  38. </u-modal>
  39. </template>
  40. <script>
  41. import { recycleSituationList } from '@/pages/wareHouse/js/public.js'
  42. import RemoteSearchSelect from '@/components/remote-search-select/index.vue'
  43. export default {
  44. name: 'ComponentName',
  45. components: {
  46. RemoteSearchSelect,
  47. },
  48. data() {
  49. return {
  50. trendModal: false,
  51. chartData: {},
  52. color: [],
  53. opts: {
  54. color: this.color,
  55. padding: [20, 10, 40, 0],
  56. dataLabel: true,
  57. dataPointShape: true,
  58. enableScroll: true,
  59. xAxis: {
  60. disableGrid: true,
  61. scrollShow: true,
  62. itemCount: 10,
  63. rotateLabel: true,
  64. rotateAngle: 45,
  65. },
  66. yAxis: {
  67. gridType: "dash",
  68. dashLength: 7,
  69. },
  70. legend: {
  71. show: false,
  72. type: 'scroll',
  73. orient: 'horizontal',
  74. pageSize: 3,
  75. pageIconSize: 12,
  76. pageIconColor: '#666',
  77. pageIconInactiveColor: '#ccc',
  78. pageTextStyle: {
  79. color: '#666',
  80. fontSize: 12
  81. },
  82. bottom: 0
  83. },
  84. extra: {
  85. line: {
  86. type: "curve",
  87. width: 3,
  88. activeType: "hollow",
  89. linearType: "custom",
  90. onShadow: true,
  91. animation: "horizontal"
  92. },
  93. tooltip: {
  94. legendShow: false,
  95. bgOpacity: 0.6,
  96. }
  97. }
  98. },
  99. model: "",
  100. maxPrice: [],
  101. minPrice: [],
  102. chartShow: false,
  103. cardData: [],
  104. // date: '',
  105. recycleSituationList: recycleSituationList,
  106. searchResults: [],
  107. loading: false,
  108. pageNum: 1,
  109. total: 0,
  110. resultsShow: false,
  111. isSelecting: false,
  112. imgResults: [],
  113. currentImg: '',
  114. imgPageNum: 1,
  115. imgTotal: 0,
  116. searchImgInfo: {},
  117. }
  118. },
  119. props: {
  120. },
  121. emits: [],
  122. methods: {
  123. handleSearch({ keyword }) {
  124. if (this.isSelecting) {
  125. this.isSelecting = false
  126. return
  127. }
  128. if (!keyword) {
  129. this.searchResults = []
  130. return
  131. }
  132. this.model = keyword
  133. this.loading = true
  134. uni.$u.api.selectModelList({
  135. inputValue: keyword,
  136. pageNum: this.pageNum,
  137. pageSize: 10,
  138. }).then(res=>{
  139. this.loading = false
  140. this.total = Number(res.total) || 0
  141. if (this.pageNum == 1){
  142. this.searchResults = res.rows || []
  143. }else{
  144. this.searchResults = [...this.searchResults, ...res.rows || []]
  145. }
  146. this.resultsShow = true
  147. })
  148. },
  149. handleFocus() {
  150. this.resultsShow = true
  151. this.handleSearch({ keyword: this.model })
  152. },
  153. handleClear() {
  154. this.resultsShow = false
  155. this.searchResults = []
  156. this.total = 0
  157. this.pageNum = 1
  158. this.imgResults = []
  159. this.imgTotal = 0
  160. this.imgPageNum = 1
  161. this.currentImg = ''
  162. this.model = ""
  163. this.minPrice = []
  164. this.maxPrice = []
  165. this.chartData = []
  166. this.cardData = []
  167. this.chartShow = false
  168. },
  169. loadMore() {
  170. if (this.searchResults.length >= this.total) {
  171. uni.$u.toast("没有更多数据了")
  172. return
  173. }
  174. this.pageNum++
  175. this.handleSearch({ keyword: this.model })
  176. },
  177. // 处理图片列表滚动到底部事件
  178. loadMoreImg() {
  179. if (this.imgResults.length >= this.imgTotal) {
  180. uni.$u.toast("没有更多数据了")
  181. return
  182. }
  183. this.imgPageNum++
  184. this.uploadImage(this.searchImgInfo)
  185. },
  186. handleSelect(item) {
  187. this.isSelecting = true
  188. this.model = item.model
  189. this.resultsShow = false
  190. this.chartShow = false
  191. this.$nextTick(() => {
  192. this.searchTrend(item.model)
  193. })
  194. },
  195. handleSelectImg(item) {
  196. this.model = item.record.model
  197. this.chartShow = false
  198. this.isSelecting = true
  199. this.pageNum = 1
  200. this.total = 0
  201. this.$nextTick(() => {
  202. this.searchTrend(item.record.model)
  203. })
  204. },
  205. handleSelectCropImg(file) {
  206. this.imgPageNum = 1
  207. this.imgTotal = 0
  208. this.imgResults = []
  209. const data = {
  210. tempFilePaths:[file]
  211. }
  212. this.uploadImage(data)
  213. },
  214. openTrendModal() {
  215. this.trendModal = true;
  216. },
  217. closeTrendModal() {
  218. this.handleClear()
  219. this.trendModal = false;
  220. },
  221. searchTrend(val) {
  222. if (val !== '') {
  223. uni.$u.api.inquiryChart({
  224. model: val,
  225. }).then(res => {
  226. if (res.data.length == 0) {
  227. uni.$u.toast("暂无数据")
  228. this.minPrice = []
  229. this.maxPrice = []
  230. this.chartShow = false
  231. this.cardData = []
  232. return
  233. }
  234. this.maxPrice = []
  235. this.minPrice = []
  236. this.color = []
  237. const categories = []
  238. const dateMap = {}
  239. const recycleData = res.data[0].list
  240. recycleData.forEach(i => {
  241. i.recycleTime = this.formatTime(i.recycleTime)
  242. if (!dateMap[i.recycleTime]) {
  243. dateMap[i.recycleTime] = true
  244. categories.push(i.recycleTime)
  245. }
  246. })
  247. const situationMap = {}
  248. recycleData.forEach(i => {
  249. const key = `${i.recycleTime}-${i.recycleSituation}`
  250. if (!situationMap[key]) {
  251. situationMap[key] = {
  252. recycleTime: i.recycleTime,
  253. recycleSituation: i.recycleSituation,
  254. prices: []
  255. }
  256. }
  257. situationMap[key].prices.push(i.price)
  258. this.maxPrice.push(i.price)
  259. this.minPrice.push(i.price)
  260. })
  261. const situationGroups = {}
  262. Object.values(situationMap).forEach(item => {
  263. const avgPrice = item.prices.reduce((sum, price) => sum + price, 0) / item.prices.length
  264. if (!situationGroups[item.recycleSituation]) {
  265. situationGroups[item.recycleSituation] = []
  266. }
  267. situationGroups[item.recycleSituation].push({
  268. recycleTime: item.recycleTime,
  269. price: avgPrice
  270. })
  271. })
  272. const series = Object.entries(situationGroups).map(([situation, data]) => {
  273. const color = this.getRandomColor()
  274. this.color.push(color)
  275. const seriesData = categories.map(date => {
  276. const itemData = data.find(i => i.recycleTime == date)
  277. return itemData ? itemData.price : null
  278. })
  279. return {
  280. name: this.formatRecycleSituation(situation),
  281. data: seriesData,
  282. list: data,
  283. setShadow: [
  284. 3,
  285. 8,
  286. 15,
  287. color
  288. ],
  289. }
  290. })
  291. this.opts = {
  292. ...this.opts,
  293. color: [...this.color]
  294. }
  295. const chartData = {
  296. categories: categories,
  297. series: series
  298. }
  299. this.chartData = chartData
  300. this.cardData = []
  301. this.chartShow = true
  302. }).catch((err) => {
  303. uni.$u.toast(err)
  304. })
  305. } else {
  306. this.minPrice = []
  307. this.maxPrice = []
  308. this.chartShow = false
  309. this.cardData = []
  310. }
  311. },
  312. uploadImage(res) {
  313. this.$nextTick(()=>{
  314. this.searchImgInfo = res;
  315. uni.$u.api.searchModelByImage(
  316. this.searchImgInfo.tempFilePaths[0],
  317. {
  318. pageNum: this.imgPageNum,
  319. pageSize: 10
  320. }
  321. ).then(res => {
  322. if (res.data) {
  323. this.imgTotal = Number(res.data.total)
  324. this.currentImg = this.searchImgInfo.tempFilePaths[0]
  325. if(this.imgPageNum == 1){
  326. this.imgResults = res.data.rows
  327. }else{
  328. this.imgResults = [...this.imgResults, ...res.data.rows]
  329. }
  330. this.$refs.searchSelect.openImgPopup()
  331. } else {
  332. uni.$u.toast('未匹配到型号,请换一张图或手动输入');
  333. }
  334. }).catch((err) => {
  335. console.log(err);
  336. uni.$u.toast('识别失败,请重试');
  337. });
  338. })
  339. },
  340. formatTime(date) {
  341. return this.$dayjs(date).format('YYYY-MM-DD')
  342. },
  343. formatRecycleSituation(situation) {
  344. const item = this.recycleSituationList.find(i => i.value == situation)
  345. return item ? item.name : '-'
  346. },
  347. handleChartClick(event) {
  348. const index = event.currentIndex.index;
  349. // 获取点击的日期
  350. const date = this.chartData.categories[index];
  351. // this.date = date;
  352. // 构建该日期的所有回收情况价格数据
  353. const cardData = [];
  354. this.chartData.series.forEach(series => {
  355. const price = series.data[index];
  356. if (price !== null) {
  357. cardData.push({
  358. model: this.model,
  359. price: price,
  360. recycleSituation: series.name,
  361. remark: '',
  362. recycleTime: date,
  363. });
  364. }
  365. });
  366. this.cardData = cardData;
  367. },
  368. getRandomColor() {
  369. var letters = '0123456789ABCDEF';
  370. var color = '#';
  371. for (var i = 0; i < 6; i++) {
  372. color += letters[Math.floor(Math.random() * 16)];
  373. }
  374. return color;
  375. },
  376. }
  377. }
  378. </script>
  379. <style lang="scss" scoped>
  380. .trend_modal {
  381. width: 100%;
  382. min-height: 500rpx;
  383. max-height: 100%;
  384. height: 100%;
  385. display: flex;
  386. flex-direction: column;
  387. gap: 20rpx;
  388. overflow-y: auto;
  389. }
  390. .charts_box {
  391. width: 100%;
  392. max-height: 600rpx;
  393. overflow: hidden;
  394. }
  395. .card-container {
  396. display: flex;
  397. flex-direction: column;
  398. gap: 16rpx;
  399. max-height: 200rpx;
  400. overflow-y: auto;
  401. font-size: 26rpx;
  402. &::-webkit-scrollbar {
  403. width: 6rpx;
  404. }
  405. &::-webkit-scrollbar-track {
  406. background: #f1f1f1;
  407. border-radius: 3rpx;
  408. }
  409. &::-webkit-scrollbar-thumb {
  410. background: #c1c1c1;
  411. border-radius: 3rpx;
  412. &:hover {
  413. background: #a8a8a8;
  414. }
  415. }
  416. }
  417. .card-item {
  418. background-color: #f5f5f5;
  419. border-radius: 8rpx;
  420. padding: 16rpx;
  421. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  422. }
  423. .card-row {
  424. display: flex;
  425. margin-bottom: 10rpx;
  426. align-items: flex-start;
  427. .card-label {
  428. flex-shrink: 0;
  429. }
  430. .card-value {
  431. flex: 1;
  432. word-break: break-all;
  433. }
  434. }
  435. </style>