index.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <view class="page">
  3. <view class="header" style="height: 70rpx;">
  4. <picker mode="selector" :range="timeTypes" :value="selectedTimeType" @change="onTimeTypeChange">
  5. <view class="time-type-picker" :class="processTimeTypes(timeTypes[selectedTimeType])">
  6. <text>{{ timeTypes[selectedTimeType] }}</text>
  7. <uni-icons type="arrowdown" size="20" color="#333" />
  8. </view>
  9. </picker>
  10. </view>
  11. <scroll-view
  12. scroll-y
  13. class="event-list"
  14. @refresherrefresh="onRefresh"
  15. :refresher-triggered="isRefreshing"
  16. refresher-enabled
  17. >
  18. <view v-for="(item, index) in filteredEventList" :key="index" class="event-card">
  19. <view class="event-header">
  20. <view :class="['status-tag', item.status]">{{ item.statusText }}</view>
  21. <text class="time">{{ item.time }}</text>
  22. </view>
  23. <view class="event-info">
  24. <view class="location">
  25. <uni-icons type="location" size="16" color="#666"/>
  26. <text class="location-text">{{ item.location }}</text>
  27. </view>
  28. <view class="type-level">
  29. <text :class="['level-tag', item.levelClass]">{{ item.level }}</text>
  30. <text class="type">{{ item.type }}</text>
  31. </view>
  32. <view class="description">{{ item.description }}</view>
  33. <view class="person-info">
  34. <text class="label">涉事人员:</text>
  35. <text>{{ item.person }}</text>
  36. </view>
  37. </view>
  38. <view class="action-buttons">
  39. <uni-button size="mini" type="warn" @click="handleEmergency(item)">
  40. <view class="button-content">
  41. <uni-icons type="phone-filled" size="16" color="#fff"/>
  42. <text>一键报警</text>
  43. </view>
  44. </uni-button>
  45. <uni-button size="mini" type="default" @click="viewDetail(item)">
  46. <view class="button-content">
  47. <uni-icons type="more-filled" size="16" color="#666"/>
  48. <text>查看详情</text>
  49. </view>
  50. </uni-button>
  51. </view>
  52. </view>
  53. </scroll-view>
  54. </view>
  55. </template>
  56. <script lang="ts" setup>
  57. import { ref, computed } from 'vue';
  58. interface EventItem {
  59. id: number;
  60. status: string;
  61. statusText: string;
  62. time: string;
  63. location: string;
  64. level: string;
  65. levelClass: string;
  66. type: string;
  67. description: string;
  68. person: string;
  69. }
  70. const eventList = ref<EventItem[]>([
  71. {
  72. id: 1,
  73. status: 'pending',
  74. statusText: '未处理',
  75. time: '2024-01-20 14:30',
  76. location: '东区停车场B2层',
  77. level: '紧急',
  78. levelClass: 'urgent',
  79. type: '安全隐患',
  80. description: '发现可疑人员在车辆周围徘徊,疑似有盗窃嫌疑',
  81. person: '刘昊林'
  82. },
  83. {
  84. id: 2,
  85. status: 'processing',
  86. statusText: '处理中',
  87. time: '2024-01-20 13:15',
  88. location: '西区3号楼电梯',
  89. level: '重要',
  90. levelClass: 'important',
  91. type: '设备故障',
  92. description: '电梯突发故障,内有人员被困',
  93. person: '郭连法'
  94. },
  95. {
  96. id: 3,
  97. status: 'completed',
  98. statusText: '已完成',
  99. time: '2024-01-20 10:20',
  100. location: '北区游泳池',
  101. level: '普通',
  102. levelClass: 'normal',
  103. type: '设施维护',
  104. description: '泳池水质异常,需要及时处理',
  105. person: '刘昊林'
  106. },
  107. {
  108. id: 4,
  109. status: 'processing',
  110. statusText: '处理中',
  111. time: '2024-01-20 13:15',
  112. location: '西区3号楼电梯',
  113. level: '重要',
  114. levelClass: 'important',
  115. type: '设备故障',
  116. description: '电梯突发故障,内有人员被困',
  117. person: '郭连法'
  118. },
  119. {
  120. id: 5,
  121. status: 'processing',
  122. statusText: '处理中',
  123. time: '2024-01-20 13:15',
  124. location: '西区3号楼电梯',
  125. level: '重要',
  126. levelClass: 'important',
  127. type: '设备故障',
  128. description: '电梯突发故障,内有人员被困',
  129. person: '郭连法'
  130. },
  131. ]);
  132. const timeTypes = ['紧急', '重要', '普通'] as const;
  133. const selectedTimeType = ref(0);
  134. const processTimeTypes = (type:typeof timeTypes[number]) => {
  135. if(type === '紧急'){
  136. return "urgent"
  137. }else if(type === '普通'){
  138. return "normal"
  139. }else if(type === '重要'){
  140. return "important"
  141. }else {
  142. return "";
  143. }
  144. }
  145. const filteredEventList = computed(() => {
  146. const selectedType = timeTypes[selectedTimeType.value];
  147. return eventList.value.filter(item => item.level === selectedType);
  148. });
  149. const handleEmergency = (item: EventItem) => {
  150. uni.showActionSheet({
  151. itemList: ['拨打110', '拨打119', '拨打120'],
  152. success: function (res) {
  153. const phoneNumbers = ['110', '119', '120'];
  154. uni.makePhoneCall({
  155. phoneNumber: phoneNumbers[res.tapIndex]
  156. });
  157. }
  158. });
  159. };
  160. const notifySecurity = (item: EventItem) => {
  161. uni.showToast({
  162. title: '已通知保安',
  163. icon: 'success'
  164. });
  165. };
  166. const notifyVisitor = (item: EventItem) => {
  167. uni.showToast({
  168. title: '已发送通知',
  169. icon: 'success'
  170. });
  171. };
  172. const viewDetail = (item: EventItem) => {
  173. uni.navigateTo({
  174. url:"/pages/eventDetail/index"
  175. })
  176. };
  177. const isRefreshing = ref(false);
  178. const onRefresh = () => {
  179. isRefreshing.value = true;
  180. setTimeout(() => {
  181. isRefreshing.value = false;
  182. uni.showToast({
  183. title: '刷新成功',
  184. icon: 'success'
  185. });
  186. }, 1000);
  187. };
  188. const onTimeTypeChange = (e: any) => {
  189. selectedTimeType.value = e.detail.value;
  190. };
  191. </script>
  192. <style>
  193. page {
  194. height: 100%;
  195. }
  196. .page {
  197. display: flex;
  198. flex-direction: column;
  199. height: 100%;
  200. background-color: #f5f5f5;
  201. }
  202. .header {
  203. display: flex;
  204. justify-content: center;
  205. align-items: center;
  206. padding: 20rpx 30rpx;
  207. background-color: #fff;
  208. border-bottom: 1px solid #eee;
  209. }
  210. .time-type-picker {
  211. display: flex;
  212. align-items: center;
  213. gap: 10rpx;
  214. background-color: #F8F9FB;
  215. padding: 10rpx 20rpx;
  216. border-radius: 10rpx;
  217. font-size: 28rpx;
  218. color: #666;
  219. }
  220. .event-list {
  221. flex: 1;
  222. overflow: auto;
  223. padding: 20rpx;
  224. }
  225. .event-card {
  226. width: 650rpx;
  227. background-color: #fff;
  228. border-radius: 12px;
  229. padding: 30rpx;
  230. margin-bottom: 20rpx;
  231. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  232. }
  233. .event-header {
  234. display: flex;
  235. justify-content: space-between;
  236. align-items: center;
  237. margin-bottom: 20rpx;
  238. }
  239. .status-tag {
  240. padding: 4rpx 16rpx;
  241. border-radius: 4px;
  242. font-size: 12px;
  243. }
  244. .pending {
  245. background-color: #ff4d4f;
  246. color: #fff;
  247. }
  248. .processing {
  249. background-color: #faad14;
  250. color: #fff;
  251. }
  252. .completed {
  253. background-color: #52c41a;
  254. color: #fff;
  255. }
  256. .time {
  257. font-size: 14px;
  258. color: #999;
  259. }
  260. .event-info {
  261. margin-bottom: 30rpx;
  262. }
  263. .location {
  264. display: flex;
  265. align-items: center;
  266. margin-bottom: 16rpx;
  267. }
  268. .location-text {
  269. margin-left: 8rpx;
  270. font-size: 14px;
  271. color: #666;
  272. }
  273. .type-level {
  274. display: flex;
  275. align-items: center;
  276. margin-bottom: 16rpx;
  277. }
  278. .level-tag {
  279. padding: 4rpx 16rpx;
  280. border-radius: 4px;
  281. font-size: 12px;
  282. margin-right: 16rpx;
  283. }
  284. .urgent {
  285. background-color: #ff4d4f;
  286. color: #fff;
  287. }
  288. .important {
  289. background-color: #faad14;
  290. color: #fff;
  291. }
  292. .normal {
  293. background-color: #1890ff;
  294. color: #fff;
  295. }
  296. .type {
  297. font-size: 14px;
  298. color: #666;
  299. }
  300. .description {
  301. font-size: 14px;
  302. color: #333;
  303. margin-bottom: 16rpx;
  304. line-height: 1.5;
  305. }
  306. .person-info {
  307. font-size: 14px;
  308. color: #666;
  309. }
  310. .label {
  311. color: #999;
  312. }
  313. .action-buttons {
  314. display: flex;
  315. justify-content: space-between;
  316. }
  317. .button-content text {
  318. margin-left: 8rpx;
  319. font-size: 12px;
  320. }
  321. .uni-button {
  322. margin: 0;
  323. flex: 1;
  324. margin-right: 16rpx;
  325. }
  326. .uni-button:last-child {
  327. margin-right: 0;
  328. }
  329. </style>