list.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <script setup lang="ts">
  2. import { reactive, onMounted, ref, nextTick } from "vue";
  3. import { getStationList, removeStation } from "@/api/station";
  4. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  5. import { ElMessage, ElMessageBox } from "element-plus";
  6. import { ExtDLabel, ExtDSelect } from "@/components/ExtForm";
  7. import StationDialog from "./dialog.vue";
  8. defineOptions({
  9. name: "AdminStationList"
  10. });
  11. const queryRef = ref();
  12. const tableRef = ref();
  13. const dialogRef = ref();
  14. const qrDialogVisible = ref(false);
  15. const currentQrCode = ref("");
  16. const currentStationName = ref("");
  17. const state = reactive({
  18. formQuery: {
  19. stationName: "",
  20. stationStatus: "",
  21. stationType: "",
  22. address: ""
  23. },
  24. pageQuery: {
  25. pageNum: 1,
  26. pageSize: 10,
  27. total: 0
  28. },
  29. tableData: {
  30. height: 500,
  31. data: [] as Array<any>,
  32. loading: false,
  33. columns: [
  34. { label: "站点名称", prop: "stationName", width: 200 },
  35. { label: "站点照片", prop: "pictures", width: 120 },
  36. { label: "站点状态", prop: "stationStatus", width: 100 },
  37. { label: "站点类型", prop: "stationType", width: 100 },
  38. { label: "地址", prop: "address", width: 250 },
  39. { label: "服务电话", prop: "serviceTel", width: 130 },
  40. { label: "营业时间", prop: "businessHours", width: 150 },
  41. { label: "工位数量", prop: "parkingNum", width: 90 },
  42. { label: "停车费用", prop: "parkingFee", width: 200 },
  43. { label: "停车减免二维码", prop: "parkingQrCode", width: 150 },
  44. { label: "创建时间", prop: "createTime", width: 160 }
  45. ]
  46. }
  47. });
  48. onMounted(() => {
  49. loadData();
  50. nextTick(() => {
  51. const bodyHeight = document.body.clientHeight;
  52. const queryHeight = queryRef.value?.$el?.clientHeight || 0;
  53. state.tableData.height = bodyHeight - queryHeight - 280;
  54. });
  55. });
  56. const loadData = (refresh: boolean = false) => {
  57. if (refresh) {
  58. state.pageQuery.pageNum = 1;
  59. }
  60. state.tableData.loading = true;
  61. getStationList({ ...state.formQuery, ...state.pageQuery })
  62. .then((res: any) => {
  63. const { list, total } = res || {};
  64. state.tableData.data = list || [];
  65. state.pageQuery.total = total || 0;
  66. })
  67. .catch(() => {
  68. state.tableData.data = [];
  69. })
  70. .finally(() => {
  71. state.tableData.loading = false;
  72. });
  73. };
  74. const handleSizeChange = (size: number) => {
  75. state.pageQuery.pageSize = size;
  76. loadData(true);
  77. };
  78. const handleCurrentChange = (page: number) => {
  79. state.pageQuery.pageNum = page;
  80. loadData();
  81. };
  82. const handleSearch = () => {
  83. loadData(true);
  84. };
  85. const handleReset = () => {
  86. state.formQuery = {
  87. stationName: "",
  88. stationStatus: "",
  89. stationType: "",
  90. address: ""
  91. };
  92. loadData(true);
  93. };
  94. const handleView = (row: any) => {
  95. dialogRef.value?.open("view", row);
  96. };
  97. const handleEdit = (row: any) => {
  98. dialogRef.value?.open("edit", row);
  99. };
  100. const handleAdd = () => {
  101. dialogRef.value?.open("add");
  102. };
  103. const handleDelete = (row: any) => {
  104. ElMessageBox.confirm(`此操作将永久删除站点『${row.stationName}』,是否继续?`, "提示", {
  105. confirmButtonText: "确定",
  106. cancelButtonText: "取消",
  107. type: "warning"
  108. }).then(() => {
  109. removeStation(row.id)
  110. .then(() => {
  111. ElMessage.success("删除成功");
  112. loadData(true);
  113. })
  114. .catch(() => {
  115. ElMessage.error("删除失败");
  116. });
  117. });
  118. };
  119. const handleShowQrCode = (row: any) => {
  120. currentQrCode.value = row.parkingQrCode;
  121. currentStationName.value = row.stationName;
  122. qrDialogVisible.value = true;
  123. };
  124. const handleGotoDevice = (row: any) => {
  125. window.location.href = `/#/admin/station/device?stationId=${row.stationId}`;
  126. };
  127. </script>
  128. <template>
  129. <div class="page-container">
  130. <el-card shadow="hover">
  131. <el-form
  132. ref="queryRef"
  133. :model="state.formQuery"
  134. inline
  135. class="search-form"
  136. >
  137. <el-form-item label="站点名称">
  138. <el-input
  139. v-model="state.formQuery.stationName"
  140. placeholder="请输入站点名称"
  141. clearable
  142. @keyup.enter="handleSearch"
  143. />
  144. </el-form-item>
  145. <el-form-item label="站点状态">
  146. <ExtDSelect
  147. v-model="state.formQuery.stationStatus"
  148. type="Station.status"
  149. placeholder="请选择站点状态"
  150. @on-change="handleSearch"
  151. />
  152. </el-form-item>
  153. <el-form-item label="站点类型">
  154. <ExtDSelect
  155. v-model="state.formQuery.stationType"
  156. type="Station.type"
  157. placeholder="请选择站点类型"
  158. @on-change="handleSearch"
  159. />
  160. </el-form-item>
  161. <el-form-item label="地址">
  162. <el-input
  163. v-model="state.formQuery.address"
  164. placeholder="请输入地址"
  165. clearable
  166. @keyup.enter="handleSearch"
  167. />
  168. </el-form-item>
  169. <el-form-item>
  170. <el-button
  171. type="primary"
  172. :icon="useRenderIcon('ri/search-line')"
  173. @click="handleSearch"
  174. >
  175. 查询
  176. </el-button>
  177. <el-button
  178. :icon="useRenderIcon('ri/refresh-line')"
  179. @click="handleReset"
  180. >
  181. 重置
  182. </el-button>
  183. <el-button
  184. type="success"
  185. :icon="useRenderIcon('ri/add-line')"
  186. @click="handleAdd"
  187. >
  188. 新增
  189. </el-button>
  190. </el-form-item>
  191. </el-form>
  192. <el-table
  193. ref="tableRef"
  194. v-loading="state.tableData.loading"
  195. :data="state.tableData.data"
  196. :height="state.tableData.height"
  197. border
  198. stripe
  199. >
  200. <template #empty>
  201. <el-empty description="暂无数据" />
  202. </template>
  203. <el-table-column
  204. v-for="col in state.tableData.columns"
  205. :key="col.prop"
  206. :prop="col.prop"
  207. :label="col.label"
  208. :width="col.width"
  209. show-overflow-tooltip
  210. >
  211. <template #default="{ row }">
  212. <template v-if="col.prop === 'stationName'">
  213. <div class="station-name-cell">
  214. <div class="station-id">{{ row.stationId }}</div>
  215. <el-divider direction="horizontal" />
  216. <div class="station-name">{{ row.stationName }}</div>
  217. </div>
  218. </template>
  219. <template v-else-if="col.prop === 'pictures'">
  220. <el-image
  221. v-if="row.pictures && row.pictures.length > 0"
  222. :src="row.pictures[0]"
  223. :preview-src-list="row.pictures"
  224. style="width: 60px; height: 60px"
  225. fit="cover"
  226. />
  227. <span v-else>-</span>
  228. </template>
  229. <template v-else-if="col.prop === 'stationStatus'">
  230. <ExtDLabel type="Station.status" :model-value="row.stationStatus" />
  231. </template>
  232. <template v-else-if="col.prop === 'stationType'">
  233. <ExtDLabel type="Station.type" :model-value="row.stationType" />
  234. </template>
  235. <template v-else-if="col.prop === 'parkingQrCode'">
  236. <el-button
  237. v-if="row.parkingQrCode"
  238. type="primary"
  239. link
  240. size="small"
  241. @click="handleShowQrCode(row)"
  242. >
  243. 查看二维码
  244. </el-button>
  245. <span v-else>-</span>
  246. </template>
  247. <template v-else>
  248. {{ row[col.prop] }}
  249. </template>
  250. </template>
  251. </el-table-column>
  252. <el-table-column label="操作" width="280" fixed="right">
  253. <template #default="{ row }">
  254. <el-button type="primary" link size="small" @click="handleGotoDevice(row)">
  255. 设备清单
  256. </el-button>
  257. <el-button type="primary" link size="small" @click="handleView(row)">
  258. 详情
  259. </el-button>
  260. <el-button type="warning" link size="small" @click="handleEdit(row)">
  261. 编辑
  262. </el-button>
  263. <el-button type="danger" link size="small" @click="handleDelete(row)">
  264. 删除
  265. </el-button>
  266. </template>
  267. </el-table-column>
  268. </el-table>
  269. <div class="pagination-container">
  270. <el-pagination
  271. v-model:current-page="state.pageQuery.pageNum"
  272. v-model:page-size="state.pageQuery.pageSize"
  273. :total="state.pageQuery.total"
  274. :page-sizes="[10, 20, 50, 100]"
  275. layout="total, sizes, prev, pager, next, jumper"
  276. @size-change="handleSizeChange"
  277. @current-change="handleCurrentChange"
  278. />
  279. </div>
  280. </el-card>
  281. <StationDialog ref="dialogRef" @refresh="loadData" />
  282. <el-dialog
  283. v-model="qrDialogVisible"
  284. :title="`${currentStationName} - 停车减免二维码`"
  285. width="400px"
  286. >
  287. <div class="qr-code-container">
  288. <img :src="currentQrCode" alt="停车减免二维码" style="width: 100%" />
  289. </div>
  290. </el-dialog>
  291. </div>
  292. </template>
  293. <style scoped lang="scss">
  294. .page-container {
  295. padding: 15px;
  296. }
  297. .search-form {
  298. margin-bottom: 15px;
  299. }
  300. .pagination-container {
  301. display: flex;
  302. justify-content: flex-end;
  303. margin-top: 15px;
  304. }
  305. .station-name-cell {
  306. text-align: center;
  307. .station-id {
  308. color: var(--el-text-color-secondary);
  309. font-size: 12px;
  310. }
  311. .station-name {
  312. font-weight: 500;
  313. }
  314. .el-divider {
  315. margin: 4px 0;
  316. }
  317. }
  318. .qr-code-container {
  319. display: flex;
  320. justify-content: center;
  321. align-items: center;
  322. padding: 20px;
  323. }
  324. </style>