list.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <template>
  2. <view>
  3. <view class="list-header flex_c_r">
  4. <view class="header-btn" v-if="isAdmin" @click="clickAdmin">
  5. <view class="icon-setting2"></view>
  6. <view>服务管理</view>
  7. </view>
  8. <view class="header-btn-close" v-else @click="clickAdmin">
  9. 退出管理
  10. </view>
  11. </view>
  12. <scroll-view refresher-enabled :refresher-triggered="isRefreshing" @refresherrefresh="onCustomRefresh"
  13. class="list-main" @scrolltolower="scrolltolower" scroll-y>
  14. <view v-if="data && data.length > 0">
  15. <view v-for="(item, index) in data" :key="index + 'lists'" class="list-item flex_c_c">
  16. <view class="item-radio-box flex_c_c" v-if="!isAdmin">
  17. <view
  18. :class="[activeIds.includes(item.volunteerServiceId) ? 'item-radio-active' : 'item-radio']"
  19. @click="activeDate(item)"></view>
  20. </view>
  21. <view class="item-main flex_c_s_l" @click="onClick(item)">
  22. <view class="item-header flex_c_s">
  23. <view class="item-title">{{ item.name }}</view>
  24. <view class="item-tags">
  25. <!-- <view v-if="item.auditStatus === '20'" :class="[SERVER_STYLE[item.auditStatus]]">{{
  26. SERVER[item.auditStatus] }}</view>
  27. <view v-else :class="[AUDITSTATUS_STYLE[item.auditStatus]]">{{
  28. AUDITSTATUS[item.auditStatus] }}</view> -->
  29. <dict-tag v-if="item.auditStatus === '20'" :options="volunteer_service_service_status" :value="String(item.serviceStatus)" />
  30. <dict-tag v-else :options="volunteer_service_audit_status" :value="String(item.auditStatus)" />
  31. </view>
  32. </view>
  33. <view class="item-text">{{ item.serviceDescribe }}</view>
  34. <view class="item-footer flex_c_s">
  35. <view class="item-price flex_c_c">
  36. <view class="dor">¥</view>
  37. <view class="price">{{ item.price }}/{{ item.unit }}
  38. </view>
  39. </view>
  40. <view class="item-date">{{ item.applyTime }}</view>
  41. </view>
  42. </view>
  43. </view>
  44. </view>
  45. <view v-else>
  46. <NoneView value="暂无服务数据"></NoneView>
  47. </view>
  48. </scroll-view>
  49. <view class="list-footer flex_s_c" v-if="!isAdmin">
  50. <view class="footer-radio-box flex_c_c" @click="activeAll()">
  51. <view
  52. :class="[(activeIds.length === data.length && activeIds.length !== 0) ? 'item-radio-active' : 'item-radio']">
  53. </view>
  54. <view class="footer-radio-text">全选</view>
  55. </view>
  56. <view class="footer-btn-add" @click="onAdd"> 新增 </view>
  57. <view class="footer-btn-up" @click="onUp"> 上线 </view>
  58. <view class="footer-btn-delete" @click="onDelete"> 删除 </view>
  59. </view>
  60. </view>
  61. </template>
  62. <script setup>
  63. import { ref } from 'vue';
  64. import { onShow } from '@dcloudio/uni-app';
  65. import NoneView from '@/components/NoneView/index.vue';
  66. import { volunteerList, volunteerServiceUp, volunteerServiceDown, volunteerServiceDelete } from '@/api/volunteer';
  67. import { useDict } from '@/utils/dict.js'
  68. import DictTag from '@/components/DictTag/index.vue'
  69. const {
  70. volunteer_service_audit_status,//志愿者服务审核状态
  71. volunteer_service_service_status,//志愿者服务服务状态
  72. } = useDict('volunteer_service_audit_status','volunteer_service_service_status');
  73. const isAdmin = ref(true);
  74. const isRefreshing = ref(false)
  75. const data = ref([
  76. // {
  77. // id: '1',
  78. // name: '老人生活-健康监测',
  79. // status: '1',
  80. // text: '依托先进的便携式检测设备与专业检测技术,打破传统检测的时空限制,可提供上门检测服务,让客户在舒适的环境中完成健康筛查',
  81. // price: '200',
  82. // desc: '次',
  83. // date: '2025-6-12 17:50'
  84. // },
  85. // {
  86. // id: '2',
  87. // name: '老人生活-健康监测',
  88. // status: '2',
  89. // text: '依托先进的便携式检测设备与专业检测技术,打破传统检测的时空限制,可提供上门检测服务,让客户在舒适的环境中完成健康筛查',
  90. // price: '200',
  91. // desc: '次',
  92. // date: '2025-6-12 17:50'
  93. // },
  94. ])
  95. const activeIds = ref([])
  96. const onCustomRefresh = async () => {
  97. isRefreshing.value = true;
  98. await getList();
  99. setTimeout(() => {
  100. isRefreshing.value = false;
  101. }, 1000);
  102. };
  103. const scrolltolower = () => {
  104. };
  105. const clickAdmin = () => {
  106. console.log(1);
  107. isAdmin.value = !isAdmin.value;
  108. };
  109. const activeDate = async (record) => {
  110. try {
  111. if (activeIds.value.includes(record.volunteerServiceId)) {
  112. activeIds.value = activeIds.value.filter(item => item !== record.volunteerServiceId)
  113. return;
  114. }
  115. activeIds.value.push(record.volunteerServiceId);
  116. } catch (error) {
  117. console.log("TCL: activeDate -> error", error)
  118. }
  119. }
  120. const activeAll = async () => {
  121. try {
  122. if (data.value.length !== activeIds.value.length) {
  123. activeIds.value = data.value.map(item => item.volunteerServiceId);
  124. return
  125. }
  126. activeIds.value = [];
  127. } catch (error) {
  128. console.log("TCL: activeAll -> error", error)
  129. }
  130. };
  131. const clearActive = () => {
  132. activeIds.value = [];
  133. getList();
  134. }
  135. const onAdd = async () => {
  136. try {
  137. uni.navigateTo({
  138. url: `/pages_home/pages/serviceManagement/index`
  139. })
  140. } catch (error) {
  141. console.log("TCL: onAdd -> error", error)
  142. }
  143. }
  144. const onUp = async () => {
  145. try {
  146. const res = await volunteerServiceUp(activeIds.value);
  147. if (res.code === 200) {
  148. uni.showToast({
  149. title: '上线成功',
  150. icon: 'success'
  151. })
  152. clearActive();
  153. return
  154. }
  155. throw res.msg || '上线失败!';
  156. } catch (error) {
  157. console.log("TCL: onUp -> error", error)
  158. uni.showToast({
  159. title: error,
  160. icon: 'none'
  161. })
  162. }
  163. }
  164. const onDelete = async () => {
  165. try {
  166. uni.showModal({
  167. title: '提示',
  168. content: '确定要删除所选服务吗?',
  169. success: async (res) => {
  170. if (res.confirm) {
  171. // 调用删除接口
  172. try {
  173. await volunteerServiceDelete(activeIds.value);
  174. uni.showToast({ title: '删除成功' });
  175. clearActive();
  176. } catch (err) {
  177. uni.showToast({ title: '删除失败', icon: 'none' });
  178. }
  179. }
  180. }
  181. });
  182. } catch (error) {
  183. console.log("TCL: onDelete -> error", error)
  184. }
  185. }
  186. const onClick = async (record) => {
  187. console.log("TCL: onClick -> record", record)
  188. try {
  189. uni.navigateTo({
  190. url: `/pages_home/pages/serviceManagement/index?volunteerServiceId=${record.volunteerServiceId}`
  191. })
  192. } catch (error) {
  193. console.log("TCL: onAdd -> error", error)
  194. }
  195. };
  196. const getList = async () => {
  197. try {
  198. const res = await volunteerList();
  199. if (res.code == 200) {
  200. console.log("TCL: getList -> res", res)
  201. data.value = res.rows;
  202. }
  203. } catch (error) {
  204. console.log("TCL: getList -> error", error)
  205. } finally {
  206. isRefreshing.value = false;
  207. }
  208. }
  209. onShow(() => {
  210. getList();
  211. })
  212. </script>
  213. <style lang="scss" scoped>
  214. @import "./index.scss";
  215. .list-main {
  216. padding: 20rpx 0;
  217. position: fixed;
  218. left: 0px;
  219. top: 92rpx;
  220. right: 0px;
  221. bottom: 0;
  222. background: #F5F5F5;
  223. overflow: hidden;
  224. overflow-y: auto;
  225. padding-bottom: 176rpx;
  226. }
  227. .list-header {
  228. height: 92rpx;
  229. padding: 0 34rpx;
  230. .header-btn {
  231. border-radius: 6rpx;
  232. background: #FFF7E8;
  233. padding: 11.2rpx 23.9rpx;
  234. font-family: PingFang SC;
  235. font-size: 28rpx;
  236. font-weight: normal;
  237. line-height: 32rpx;
  238. display: flex;
  239. align-items: center;
  240. letter-spacing: normal;
  241. color: #FF7D00;
  242. .icon-setting {
  243. margin-right: 6rpx;
  244. }
  245. }
  246. .header-btn-close {
  247. @extend .header-btn;
  248. color: #4E5969;
  249. background: #F2F3F5;
  250. }
  251. }
  252. .item-radio {
  253. width: 34rpx;
  254. height: 34rpx;
  255. border-radius: 34rpx;
  256. border: 4rpx solid #C4C4C4;
  257. }
  258. .item-radio-active {
  259. @extend .item-radio;
  260. border: none;
  261. @extend .whether-radio-active;
  262. }
  263. .list-item {
  264. padding: 24rpx 40rpx;
  265. margin-bottom: 20rpx;
  266. background: #fff;
  267. gap: 44rpx;
  268. .item-radio-box {
  269. min-width: 44rpx;
  270. }
  271. .item-main {
  272. flex: 1;
  273. gap: 22rpx;
  274. .item-header {
  275. width: 100%;
  276. .item-title {
  277. margin-top: 24rpx;
  278. font-family: PingFang SC;
  279. font-size: 36rpx;
  280. font-weight: normal;
  281. line-height: 44rpx;
  282. letter-spacing: normal;
  283. /* 外部/Neutral/10强调、正文标题 */
  284. color: #1D2129;
  285. }
  286. .item-tags {
  287. }
  288. }
  289. .item-text {
  290. font-family: PingFang SC;
  291. font-size: 30rpx;
  292. font-weight: normal;
  293. line-height: 40rpx;
  294. text-align: justify;
  295. /* 浏览器可能不支持 */
  296. letter-spacing: normal;
  297. color: #86909C;
  298. display: -webkit-box;
  299. -webkit-box-orient: vertical;
  300. -webkit-line-clamp: 3;
  301. overflow: hidden;
  302. text-overflow: ellipsis;
  303. width: 100%;
  304. }
  305. .item-footer {
  306. width: 100%;
  307. .item-price {
  308. font-family: PingFang SC;
  309. font-size: 32rpx;
  310. font-weight: normal;
  311. line-height: 42rpx;
  312. text-align: right;
  313. letter-spacing: normal;
  314. color: #F53F3F;
  315. /* ¥ */
  316. .dor {
  317. font-family: PingFang SC;
  318. font-weight: 400;
  319. font-size: 24rpx;
  320. font-variation-settings: "opsz" auto;
  321. }
  322. /* 200/次 */
  323. .price {
  324. font-family: PingFang SC;
  325. font-weight: 400;
  326. font-size: 32rpx;
  327. font-variation-settings: "opsz" auto;
  328. }
  329. }
  330. .item-date {
  331. font-family: PingFang SC;
  332. font-size: 28rpx;
  333. font-weight: normal;
  334. line-height: 40rpx;
  335. letter-spacing: normal;
  336. color: #86909C;
  337. }
  338. }
  339. }
  340. }
  341. .list-footer {
  342. position: fixed;
  343. bottom: 0rpx;
  344. left: 0px;
  345. right: 0;
  346. z-index: 999;
  347. background: #fff;
  348. padding-bottom: 60rpx;
  349. height: 176rpx;
  350. gap: 30rpx;
  351. padding: 24rpx 50rpx;
  352. .footer-radio-box {
  353. margin-top: 28rpx;
  354. margin-right: 25rpx;
  355. .footer-radio-text {
  356. font-family: PingFang SC;
  357. font-size: 32rpx;
  358. font-weight: normal;
  359. line-height: 44rpx;
  360. text-align: right;
  361. display: flex;
  362. align-items: center;
  363. letter-spacing: normal;
  364. /* 文字/text-5 */
  365. color: #1D2129;
  366. margin-left: 6rpx;
  367. }
  368. }
  369. .footer-btn {
  370. border-radius: 200rpx;
  371. box-sizing: border-box;
  372. /* 错误/danger-6 */
  373. border: 2rpx solid;
  374. padding: 15rpx 32rpx;
  375. font-family: PingFang SC;
  376. font-size: 30rpx;
  377. font-weight: normal;
  378. line-height: 42rpx;
  379. display: flex;
  380. align-items: center;
  381. letter-spacing: normal;
  382. }
  383. .footer-btn-add {
  384. @extend .footer-btn;
  385. border-color: #F53F3F;
  386. color: #F53F3F;
  387. }
  388. .footer-btn-up {
  389. @extend .footer-btn;
  390. border-color: #FFECE8;
  391. background: #FFECE8;
  392. color: #F53F3F;
  393. }
  394. .footer-btn-delete {
  395. @extend .footer-btn;
  396. border-color: #F53F3F;
  397. background: #F53F3F;
  398. color: #fff;
  399. }
  400. }
  401. </style>