index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. <template>
  2. <ay-container v-show="store.user.isLogined">
  3. <div class="chunk">
  4. <div class="center justify-between top">
  5. <div class="center" @click="methods.goPerson">
  6. <image class="personal" :src="store.user.userInfo.head_url" mode="scaleToFill" />
  7. <div>
  8. <div>{{ store.user.userInfo.user_name }}</div>
  9. <div class="mobile">{{ store.user.userInfo.mobileDes }}</div>
  10. </div>
  11. </div>
  12. <uni-icons
  13. type="scan"
  14. color=""
  15. class="p-color"
  16. size="40"
  17. @click="methods.scanCode"
  18. ></uni-icons>
  19. </div>
  20. <div class="center">
  21. <ay-flowLine class="mt-spac mb-spac" :loading="store.webapi.strategy.get_user_qrcode.ing">
  22. <div class="qrcode">
  23. <image
  24. src="@img/qr@3x.png"
  25. class="image"
  26. mode="aspectFit"
  27. v-if="data.hasnopayfordriver || data.qrcode.expire"
  28. @click="methods.getQrcode"
  29. />
  30. <image :src="data.qrcode.img" class="image" mode="aspectFit" v-else />
  31. </div>
  32. </ay-flowLine>
  33. </div>
  34. <div class="center qrcode-text">
  35. <template v-if="!data.firstUnload">
  36. <template v-if="data.hasnopayfordriver">存在未支付完成订单</template>
  37. <template v-else-if="data.qrcode.expire">
  38. 二维码已失效
  39. <ay-refresh
  40. :iconSize="36"
  41. :loading="store.webapi.strategy.get_user_qrcode.ing"
  42. @refresh="methods.getQrcode"
  43. ></ay-refresh>
  44. </template>
  45. <template v-else>
  46. 有效时间:
  47. <ay-countdown :seconds="data.qrcode.seconds" @ender="data.qrcode.expire = true" />
  48. </template>
  49. </template>
  50. </div>
  51. </div>
  52. <div class="flex">
  53. <div class="chunk flex-1 s-fz mr-spac money-bg" @click="methods.goAccount">
  54. <div class="mb-spacd4">可用余额</div>
  55. <div class="">
  56. <span class="font-bold b-fz">{{ data.accountInfo.showBalance.balance }}</span>
  57. <span class="font-bold">{{ data.accountInfo.showBalance.suffix }}</span>
  58. <span class="unit">{{ data.accountInfo.unit }}</span>
  59. </div>
  60. </div>
  61. <div class="chunk flex-1 center justify-start s-fz cars" @click="methods.truckManage">
  62. <div class="p-color" v-if="data.truckInfo.length === 0">添加车辆</div>
  63. <div v-else>
  64. <div class="mb-spacd4">车辆管理</div>
  65. <div>
  66. <span class="font-bold b-fz">{{ data.truckInfo.length }}</span>
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. <div class="center justify-around mt-spac">
  72. <div
  73. class="text-center menu"
  74. v-for="(m, i) in data.menus"
  75. :key="i"
  76. @click="methods.goPage(m)"
  77. >
  78. <img class="icon" :src="m.icon" />
  79. <div>{{ m.name }}</div>
  80. </div>
  81. </div>
  82. <div class="chuck list-app">
  83. <div
  84. class="center justify-between sticky"
  85. id="stationTop"
  86. :class="{ stickyed: data.station.stickyed }"
  87. >
  88. <div class="title">附近加气站</div>
  89. <div v-if="data.userLocation" @click="methods.drawerOpen">
  90. {{ data.station.curCity }}
  91. <uni-icons type="down" size="12" />
  92. </div>
  93. </div>
  94. <div v-show="data.userLocation" id="stations">
  95. <ayb-station
  96. v-for="(la, i) in data.station.listApp"
  97. :key="i"
  98. :stationInfo="la"
  99. ></ayb-station>
  100. </div>
  101. <div
  102. class="center p-color"
  103. v-show="!data.userLocation"
  104. @click="methods.getListApp({ first: true })"
  105. >
  106. 查看附近加气站
  107. </div>
  108. </div>
  109. <uni-popup ref="inputDialog" type="dialog">
  110. <uni-popup-dialog
  111. mode="input"
  112. title="邀请通知"
  113. confirmText="加入"
  114. cancelText="拒绝"
  115. :beforeClose="true"
  116. @confirm="methods.agree"
  117. @close="methods.disagree"
  118. >
  119. <div class="text-center w-100%">
  120. <div class="mb-spacd2 text-center">
  121. 【{{ data.invite?.org?.orgName }}】邀请您加入成为其司机
  122. <span class="p-color" @click="methods.phone(data.invite?.org?.mobile)">
  123. <uni-icons type="phone" color="" class="mr-spacd4" size="none"></uni-icons>
  124. <span>{{ data.invite?.org?.mobile }}</span>
  125. </span>
  126. </div>
  127. </div>
  128. </uni-popup-dialog>
  129. </uni-popup>
  130. <uni-drawer
  131. ref="drawer"
  132. mode="right"
  133. :maskClick="true"
  134. :width="320"
  135. @change="methods.drawerChange"
  136. >
  137. <uni-indexed-list
  138. :options="data.station.cityList"
  139. :show-select="false"
  140. @click="methods.changeCity"
  141. ref="indexedList"
  142. v-if="data.station.indexedListShow"
  143. />
  144. </uni-drawer>
  145. <ayb-carNumber ref="carNumber" />
  146. </ay-container>
  147. </template>
  148. <script lang="ts" setup>
  149. import stompSocket from '@/utils/api/socket/stomp'
  150. import perpay from '@img/icons/perpay.png'
  151. import payhistory from '@img/icons/payhistory.png'
  152. import firm from '@img/icons/firm.png'
  153. import card from '@img/icons/card.png'
  154. const drawer = ref()
  155. const indexedList = ref()
  156. const carNumber = ref()
  157. const inputDialog = ref()
  158. const staticData = {
  159. /** 选择的城市 缓存key名 */
  160. cctkn: 'chooseCity',
  161. /** 默认选择城市 */
  162. defCity: '全部',
  163. /** websocket 实例 */
  164. client: null,
  165. // entranceArgs: null as AyContainerEntryArgs,
  166. /** 临时记录位置信息 */
  167. location: { longitude: '', latitude: '' } as any,
  168. indexedListShowed: false,
  169. /** 索引组件数据监听器 */
  170. indexedListWatcher: null,
  171. /** 页面滚动距离 */
  172. scrollTop: 0,
  173. }
  174. const data = reactive({
  175. firstUnload: true,
  176. // 存在未支付完成订单
  177. hasnopayfordriver: true,
  178. invite: {} as 预添加司机详情,
  179. truckInfo: [] as PersonDriver对象[],
  180. accountInfo: { showBalance: { balance: '', suffix: '' } } as IAccountInfo,
  181. /** 用户已授权定位 */
  182. userLocation: false,
  183. /** 二维码相关 */
  184. qrcode: {
  185. /** 有效时长 m */
  186. seconds: 300, // 10,
  187. /** 二维码 */
  188. img: null,
  189. /** 已过期 */
  190. expire: true,
  191. },
  192. /** 菜单列表 */
  193. menus: [
  194. {
  195. id: 0,
  196. icon: perpay,
  197. name: '待支付订单',
  198. },
  199. {
  200. id: 1,
  201. name: '加气订单',
  202. icon: payhistory,
  203. },
  204. {
  205. id: 2,
  206. name: '所属物流',
  207. icon: firm,
  208. },
  209. {
  210. id: 3,
  211. name: '优惠卡/券',
  212. icon: card,
  213. },
  214. ],
  215. station: {
  216. /** 头部已吸顶 */
  217. stickyed: false,
  218. cityList: [] /** 附近加气站列表 */,
  219. curCity: staticData.defCity,
  220. listApp: [],
  221. indexedListShow: false,
  222. },
  223. })
  224. const methods = ay.initMethods(
  225. {
  226. phone(num) {
  227. uni.makePhoneCall({
  228. phoneNumber: num,
  229. })
  230. },
  231. getTruckInfo() {
  232. webapi.strategy.get_truck_info().then((res) => {
  233. data.truckInfo = aop.request.AR.truckInfo(res)
  234. })
  235. },
  236. truckManage() {
  237. if (data.truckInfo.length) {
  238. ay.goPage(config.pages.truckInfo_index)
  239. } else {
  240. carNumber.value.open().then((cn) => {
  241. webapi.strategy
  242. .bind_person_truck<{ userId: string }>({
  243. userId: store.user.userInfo.user_id,
  244. newCarNumber: cn,
  245. })
  246. .then((res) => {
  247. if (res) {
  248. func.native.showToast('添加成功')
  249. methods.getTruckInfo()
  250. }
  251. })
  252. })
  253. }
  254. },
  255. goPerson() {
  256. ay.goPage(config.pages.personCenter_index)
  257. },
  258. goPage(menu) {
  259. const maps = {
  260. 0() {
  261. methods.checkNopayfordriver(1)
  262. },
  263. 1() {
  264. ay.goPage(config.pages.order_index)
  265. },
  266. 2() {
  267. ay.goPage(config.pages.org_index)
  268. },
  269. }
  270. maps[menu.id]()
  271. },
  272. drawerOpen() {
  273. drawer.value.open()
  274. data.station.indexedListShow = true
  275. // 解决uni-indexed-list 组件滑动失效问题
  276. // 问题原因: 因为嵌套于uni-drawer内,造成uni-indexed-list的winOffsetY值获取有误
  277. // 解决方法:通过阅读组件源码发现uni-indexed-list在初始化时会设置winOffsetY,再给他设置正确的值。
  278. // 小程序于h5环境设置时机不同;
  279. // 组件内部:小程序仅首次渲染完毕会设置winOffsetY,h5环境每次打开组件都会重新设置winOffsetY
  280. // 通过监听$data.loaded的变化,重新设置winOffsetY为页面滚动距离,以及小程序环境-每次呈现组件后重新设置winOffsetY
  281. nextTick(() => {
  282. // 小程序环境-每次呈现组件后重新设置winOffsetY
  283. // #ifdef MP
  284. if (indexedList.value) {
  285. indexedList.value.$data.winOffsetY = staticData.scrollTop
  286. }
  287. // #endif
  288. if (!staticData.indexedListWatcher) {
  289. staticData.indexedListWatcher = watch(
  290. () => indexedList.value?.$data.loaded,
  291. (nv) => {
  292. // 小程序环境仅首次呈现监听到, h5环境每次呈现都会监听到
  293. if (indexedList.value) {
  294. indexedList.value.$data.winOffsetY = staticData.scrollTop
  295. }
  296. },
  297. {
  298. // deep: true,
  299. },
  300. )
  301. // 调用可释放监听
  302. // staticData.indexedListWatcher()
  303. }
  304. })
  305. },
  306. drawerChange(isopen) {},
  307. changeCity(args) {
  308. drawer.value.close()
  309. data.station.curCity = args.item.name
  310. uni.setStorageSync(staticData.cctkn, args.item.name)
  311. methods.getListApp()
  312. },
  313. goAccount() {
  314. ay.goPage(config.pages.account_index)
  315. },
  316. /** 初始化ws */
  317. initWS() {
  318. stompSocket
  319. .init(
  320. 'wss://dwx.auyen.com/websocket/sockjs',
  321. // 传参
  322. {
  323. access_token: store.user.userInfo.token,
  324. identifier: store.user.userInfo.Identifier,
  325. },
  326. // ws断开回调
  327. () => {
  328. methods.initWS()
  329. },
  330. )
  331. .then((client) => {
  332. staticData.client = client
  333. // 订阅
  334. const subscription = client.subscribe(
  335. // 路径
  336. '/user/' + store.user.userInfo.user_id + '/msg',
  337. // 接收到的数据
  338. (res) => {
  339. const body = JSON.parse(res.body)
  340. if (body.type === 1) {
  341. ay.goPage(config.pages.order_prePay, { params: JSON.parse(body.content) })
  342. } else if (body.type === 0) {
  343. const content = JSON.parse(body.content)
  344. uni
  345. .showModal({
  346. title: content.data,
  347. message: content.message,
  348. })
  349. .then(() => {})
  350. }
  351. },
  352. )
  353. })
  354. },
  355. // 直接调用发送即可
  356. send() {
  357. staticData.client.send(
  358. // 路径
  359. '/user/' + store.user.userInfo.user_id + '/msg',
  360. {},
  361. // 发送文本
  362. JSON.stringify({ content: '1212' }),
  363. )
  364. },
  365. /** 检查未支付
  366. * type 0:仅检查 1:点击待支付订单 2:点击扫码
  367. * */
  368. async checkNopayfordriver(type: 0 | 1 | 2 = 0) {
  369. const nopayfordriver = await webapi.pay.find_unpayfordriver()
  370. // 存在 未支付(待支付、支付中、支付异常)
  371. if (nopayfordriver) {
  372. if (type === 1) {
  373. if (nopayfordriver.settleStatus === 1) {
  374. // 跳未支付
  375. ay.goPage(config.pages.order_prePay, { params: { orderId: nopayfordriver.orderId } })
  376. }
  377. }
  378. if (type === 2) {
  379. func.native
  380. .showModal({
  381. title: '温馨提示',
  382. content: '存在未支付完成订单',
  383. showCancel: true,
  384. })
  385. .then((res) => {
  386. if (res.confirm) {
  387. if (nopayfordriver.settleStatus === 1) {
  388. ay.goPage(config.pages.order_prePay, {
  389. params: { orderId: nopayfordriver.orderId },
  390. })
  391. } else {
  392. ay.goPage(config.pages.order_orderDetail, {
  393. params: { orderId: nopayfordriver.orderId },
  394. })
  395. }
  396. }
  397. })
  398. }
  399. return true
  400. } else {
  401. if (type === 1) {
  402. func.native.showToast('暂无待支付订单!')
  403. }
  404. return false
  405. }
  406. },
  407. async scanCode() {
  408. if (await methods.checkNopayfordriver(2)) {
  409. return
  410. }
  411. const ret: { gasstationId?: string; cashierId?: string } = await func.native.scan()
  412. if (!(ret.gasstationId && ret.cashierId)) {
  413. func.native.showToast('二维码无效')
  414. }
  415. const user = await webapi.user.find_6({ userId: ret.cashierId })
  416. if (ret.gasstationId === user.user.orgId) {
  417. const price = await webapi.strategy.find_price({
  418. gasstationId: ret.gasstationId,
  419. driverId: user.user.userId,
  420. })
  421. if (
  422. ((price.qrcode === 0 || price.qrcode === 1) && ret.gasstationId !== ret.cashierId) || // 设置的加气站收款码
  423. (price.qrcode === 2 && ret.gasstationId === ret.cashierId) // 设置的收银员
  424. ) {
  425. func.native.showModal({
  426. title: '错误提示',
  427. content: '加气站收款码已禁用,请选择收银员收款码扫码。',
  428. })
  429. } else {
  430. const gasJudge = await webapi.strategy.driver_gas_judge({
  431. driverId: user.user.userId,
  432. gasstationId: ret.gasstationId,
  433. })
  434. if (gasJudge === 1) {
  435. // 创建订单
  436. ay.goPage(config.pages.order_createOrder, { params: ret })
  437. } else if (gasJudge === 2) {
  438. func.native.showModal({
  439. title: '错误提示',
  440. content:
  441. '该加气站未及时补充平台气源,已无法通过大象平台为您提供优质气源,给您带来的不便深感其歉意,如有疑问可以与加气站沟通,亦可拨打大象加气平台客服电话:"400-0165388"',
  442. })
  443. }
  444. }
  445. } else {
  446. func.native.showModal({
  447. title: '错误提示',
  448. content: '当前收银员所属企业和二维码对应的企业不一致,请联系加气站管理员。',
  449. })
  450. }
  451. },
  452. /** 同意加入 */
  453. agree() {
  454. webapi.user.agree({ id: data.invite.driverWhiteList.id }).then((res) => {
  455. if (res) {
  456. func.native.showToast('加入成功,请重新登录').then(() => {
  457. store.user.clearUserInfo()
  458. })
  459. inputDialog.value.close()
  460. }
  461. })
  462. },
  463. /** 拒绝加入 */
  464. disagree() {
  465. webapi.user.disagree({ id: data.invite.driverWhiteList.id }).then((res) => {
  466. if (res) {
  467. func.native.showToast('已拒绝')
  468. inputDialog.value.close()
  469. }
  470. })
  471. },
  472. /** 获取邀请信息 */
  473. getInvite() {
  474. webapi.user
  475. .find_by_driver_mobile({ driverMobile: store.user.userInfo.mobile })
  476. .then((res) => {
  477. if (res.driverWhiteList && res.org) {
  478. data.invite = res
  479. inputDialog.value.open()
  480. }
  481. })
  482. },
  483. getCityList() {
  484. webapi.strategy.city_list().then((res) => {
  485. res.unshift({ text: staticData.defCity, children: [{ text: staticData.defCity }] })
  486. data.station.cityList = res.map((m) => {
  487. return {
  488. letter: m.text,
  489. data: m.children.map((cm) => cm.text),
  490. }
  491. })
  492. })
  493. },
  494. async getListApp({ cras = config.common.defAyContainerRefreshArgs, first = false } = {}) {
  495. // 非首次 && 未授权
  496. if (!first && !data.userLocation) {
  497. // 取消加载中效果
  498. ay.containerLoaded({
  499. reqState: enums.ReqState.cancel,
  500. })
  501. return
  502. }
  503. let location = staticData.location
  504. // 上拉不重新获取位置
  505. if (!cras.isAdd) {
  506. location = await func.native.getLocation()
  507. staticData.location = location
  508. }
  509. data.userLocation = true
  510. webapi.strategy
  511. .list_mini(
  512. {
  513. page: cras.page,
  514. size: cras.size,
  515. param: {
  516. city: data.station.curCity === staticData.defCity ? '' : data.station.curCity,
  517. longitude: location.longitude.toString(),
  518. latitude: location.latitude.toString(),
  519. },
  520. },
  521. { showLoading: first }, // || !cras.isAdd },
  522. )
  523. .then((res) => {
  524. if (cras.isAdd) {
  525. data.station.listApp.push(...res.records)
  526. } else {
  527. data.station.listApp = res.records
  528. }
  529. })
  530. },
  531. getQrcode() {
  532. if (data.hasnopayfordriver || store.webapi.strategy.get_user_qrcode.ing) {
  533. return
  534. }
  535. return webapi.strategy.get_user_qrcode({}, { minRTime: 1000 }).then(async (res) => {
  536. data.qrcode.img = res
  537. data.qrcode.expire = false
  538. })
  539. },
  540. },
  541. {
  542. scanCode: { showLoading: true },
  543. },
  544. )
  545. ay.entrance(
  546. async (args) => {
  547. // staticData.entranceArgs = args
  548. const init = async () => {
  549. webapi.strategy.get_driver_balance().then((res) => {
  550. data.accountInfo = {
  551. ...res,
  552. ...aop.request.AR.getAccountInfo(res),
  553. }
  554. })
  555. // #ifdef MP
  556. // 用户是否授权了获取地理位置
  557. await uni
  558. .getSetting()
  559. .then((res) => {
  560. if (res.authSetting['scope.userLocation']) {
  561. data.userLocation = true
  562. }
  563. })
  564. .catch()
  565. // #endif
  566. methods.getListApp({ cras: args.cras })
  567. methods.getTruckInfo()
  568. const nopayfordriver = await methods.checkNopayfordriver()
  569. data.hasnopayfordriver = nopayfordriver
  570. }
  571. if (args.loadType === enums.LoadType.onLoad) {
  572. methods.initWS()
  573. methods.getCityList()
  574. data.station.curCity = uni.getStorageSync(staticData.cctkn) || staticData.defCity
  575. await init()
  576. // 无未支付订单
  577. if (!data.hasnopayfordriver) {
  578. await methods.getQrcode()
  579. }
  580. data.firstUnload = false
  581. // 小程序环境:时机过早会inputDialog.value为null
  582. // h5环境:当页面data内的值改变时,弹框会消失
  583. // 所有先放这里吧
  584. methods.getInvite()
  585. }
  586. if (args.loadType === enums.LoadType.refresh) {
  587. if (args.cras.isAdd) {
  588. methods.getListApp({ cras: args.cras })
  589. } else {
  590. // 下拉更新
  591. init()
  592. }
  593. }
  594. },
  595. {
  596. // addLoadTypes: [enums.LoadType.onShow],
  597. },
  598. )
  599. onMounted(() => {
  600. // #ifdef MP
  601. const iob = uni.createIntersectionObserver(getCurrentInstance())
  602. iob.relativeTo('#stations').observe('#stationTop', (res) => {
  603. data.station.stickyed = res.intersectionRatio > 0
  604. })
  605. // #endif
  606. // uni
  607. // .createSelectorQuery()
  608. // .select('#stationTop')
  609. // // .boundingClientRect()
  610. // .fields(
  611. // {
  612. // scrollOffset: true,
  613. // rect:true,
  614. // },
  615. // (res) => {
  616. // console.log(res)
  617. // },
  618. // )
  619. // .exec((res) => {
  620. // console.log(res)
  621. // })
  622. })
  623. onPageScroll((res) => {
  624. staticData.scrollTop = res.scrollTop
  625. })
  626. </script>
  627. <style lang="scss" scoped>
  628. .top {
  629. padding-bottom: $p-spac;
  630. border-bottom: 1px dashed;
  631. .personal {
  632. width: 100rpx;
  633. height: 100rpx;
  634. margin-right: $p-spac;
  635. border-radius: $p-spac;
  636. }
  637. .mobile {
  638. margin-top: $p-spacd2;
  639. font-size: $s-fz;
  640. }
  641. }
  642. .qrcode {
  643. width: 400rpx;
  644. height: 400rpx;
  645. overflow: hidden;
  646. border-radius: $p-spacd2;
  647. .image {
  648. width: 480rpx;
  649. height: 480rpx;
  650. margin: -40rpx;
  651. }
  652. }
  653. .qrcode-text {
  654. height: $p-fz;
  655. }
  656. .truck {
  657. width: 152rpx;
  658. height: 116rpx;
  659. }
  660. #stationTop {
  661. top: -1px;
  662. z-index: 1;
  663. padding: $p-spac 0;
  664. background: #fff;
  665. &.stickyed {
  666. border-bottom: 1rpx solid $border-color;
  667. @extend %box-shadow;
  668. &::before,
  669. &::after {
  670. position: absolute;
  671. width: $p-spac;
  672. height: 100%;
  673. content: '';
  674. background-color: #fff;
  675. border-bottom: 1rpx solid $border-color;
  676. }
  677. &::before {
  678. left: -$p-spac;
  679. }
  680. &::after {
  681. right: -$p-spac;
  682. }
  683. }
  684. }
  685. .unit {
  686. margin: 0 $p-spacd2;
  687. }
  688. $bgc: rgba($p-color, 0.4);
  689. .cars {
  690. position: relative;
  691. overflow: hidden;
  692. &::after {
  693. position: absolute;
  694. top: 0;
  695. left: 0;
  696. width: 100%;
  697. height: 100%;
  698. content: '';
  699. background-image: linear-gradient($bgc, $bgc), url('@img/truck.svg');
  700. background-repeat: no-repeat;
  701. background-position: right;
  702. background-size: contain;
  703. opacity: 0.2;
  704. }
  705. }
  706. .menu {
  707. font-size: $s-fz;
  708. .icon {
  709. width: 80rpx;
  710. height: 80rpx;
  711. margin-bottom: $p-spacd2;
  712. border-radius: 50%;
  713. }
  714. }
  715. .list-app {
  716. margin-top: $p-spac;
  717. .title {
  718. font-size: $p-fz * 1.2;
  719. font-weight: bold;
  720. }
  721. }
  722. // 为了让左侧索引中文竖排
  723. ::v-deep .uni-indexed-list__menu-item {
  724. width: 20px;
  725. }
  726. ::v-deep .uni-popup__info {
  727. @apply: font-bold;
  728. font-size: $bs-fz;
  729. color: unset;
  730. }
  731. </style>