index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <template>
  2. <div class="station-page">
  3. <a-card :bordered="false">
  4. <a-form :model="queryParams" layout="inline">
  5. <a-form-item label="场站名称">
  6. <a-input v-model:value="queryParams.stationName" allow-clear placeholder="请输入场站名称" />
  7. </a-form-item>
  8. <a-form-item label="场站编码">
  9. <a-input v-model:value="queryParams.stationCode" allow-clear placeholder="请输入场站编码" />
  10. </a-form-item>
  11. <a-form-item label="场站类型">
  12. <a-select v-model:value="queryParams.stationType" allow-clear class="query-select" :options="stationTypeOptions" />
  13. </a-form-item>
  14. <a-form-item label="是否启用">
  15. <a-select v-model:value="queryParams.isEnable" allow-clear class="query-select" :options="enableOptions" />
  16. </a-form-item>
  17. <a-form-item>
  18. <a-space>
  19. <a-button type="primary" :loading="loading" @click="handleQuery"><BearJiaIcon icon="SearchOutlined"/>查询</a-button>
  20. <a-button @click="resetQuery">重置</a-button>
  21. </a-space>
  22. </a-form-item>
  23. </a-form>
  24. </a-card>
  25. <a-card ref="tableCardRef" :bordered="false" class="section">
  26. <TableToolbar
  27. :columns="columns"
  28. :selected-columns="selectedColumnKeys"
  29. :loading="loading"
  30. :export-config="exportConfig"
  31. @columnSettingsChange="handleColumnSettingsChange"
  32. @refresh="handleQuery"
  33. @fullscreen="handleFullscreen"
  34. @export="handleExport"
  35. @reset="handleTableReset"
  36. >
  37. <template #left>
  38. <a-button v-hasPermi="['config:station:add']" type="primary" @click="openForm()"><BearJiaIcon icon="plus-outlined"/>新增</a-button>
  39. <a-button v-hasPermi="['config:station:remove']" danger type="primary" :disabled="selectedRowKeys.length <= 0" @click="handleBatchDelete"><BearJiaIcon icon="delete-outlined"/>删除</a-button>
  40. <a-button v-hasPermi="['config:station:export']" @click="handleExport"><BearJiaIcon icon="export-outlined"/>导出</a-button>
  41. </template>
  42. </TableToolbar>
  43. <a-table
  44. :columns="displayColumns"
  45. :data-source="rows"
  46. :loading="loading"
  47. :pagination="pagination"
  48. row-key="id"
  49. :row-selection="rowSelection"
  50. :size="tableConfigStore.size"
  51. @change="handleTableChange"
  52. >
  53. <template #bodyCell="{ column, record }">
  54. <template v-if="column.key === 'stationType'">
  55. {{ stationTypeText(record.stationType) }}
  56. </template>
  57. <template v-else-if="column.key === 'isEnable'">
  58. <a-tag :color="record.isEnable === '1' ? 'success' : 'default'">{{ enableText(record.isEnable) }}</a-tag>
  59. </template>
  60. <template v-else-if="column.key === 'operation'">
  61. <TableActionBar :record="record" :has-view="false" :has-edit="true" :has-delete="true">
  62. <template #edit>
  63. <a v-hasPermi="['config:station:edit']" class="action-btn edit-btn" @click="openForm(record)"><BearJiaIcon icon="edit-outlined"/>修改</a>
  64. </template>
  65. <template #delete>
  66. <a-divider type="vertical"/>
  67. <a v-hasPermi="['config:station:remove']" class="action-btn delete-btn" @click="handleDeleteRecord(record)"><BearJiaIcon icon="delete-outlined"/>删除</a>
  68. </template>
  69. </TableActionBar>
  70. </template>
  71. </template>
  72. </a-table>
  73. </a-card>
  74. <a-modal v-model:open="formOpen" :title="form.id ? '编辑场站' : '新增场站'" width="820px" @ok="submitForm">
  75. <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
  76. <a-row :gutter="16">
  77. <a-col :span="12">
  78. <a-form-item label="场站名称" name="stationName">
  79. <a-input v-model:value="form.stationName" allow-clear />
  80. </a-form-item>
  81. </a-col>
  82. <a-col :span="12">
  83. <a-form-item label="场站编码" name="stationCode">
  84. <a-input v-model:value="form.stationCode" allow-clear />
  85. </a-form-item>
  86. </a-col>
  87. <a-col :span="12">
  88. <a-form-item label="场站类型" name="stationType">
  89. <a-select v-model:value="form.stationType" allow-clear :options="stationTypeOptions" />
  90. </a-form-item>
  91. </a-col>
  92. <a-col :span="12">
  93. <a-form-item label="是否启用" name="isEnable">
  94. <a-select v-model:value="form.isEnable" allow-clear :options="enableOptions" />
  95. </a-form-item>
  96. </a-col>
  97. <a-col :span="12">
  98. <a-form-item label="装机容量(kW)" name="installedCapacity">
  99. <a-input-number v-model:value="form.installedCapacity" class="full-input" :min="0" />
  100. </a-form-item>
  101. </a-col>
  102. <a-col :span="12">
  103. <a-form-item label="并网容量(kW)" name="gridConnectedCapacity">
  104. <a-input-number v-model:value="form.gridConnectedCapacity" class="full-input" :min="0" />
  105. </a-form-item>
  106. </a-col>
  107. <a-col :span="12">
  108. <a-form-item label="经度" name="longitude">
  109. <a-input v-model:value="form.longitude" allow-clear />
  110. </a-form-item>
  111. </a-col>
  112. <a-col :span="12">
  113. <a-form-item label="纬度" name="latitude">
  114. <a-input v-model:value="form.latitude" allow-clear />
  115. </a-form-item>
  116. </a-col>
  117. <a-col :span="12">
  118. <a-form-item label="位置" name="position">
  119. <a-input v-model:value="form.position" allow-clear />
  120. </a-form-item>
  121. </a-col>
  122. <a-col :span="12">
  123. <a-form-item label="投运时间" name="operationTime">
  124. <a-input v-model:value="form.operationTime" allow-clear placeholder="如 2026-01-01" />
  125. </a-form-item>
  126. </a-col>
  127. <a-col :span="12">
  128. <a-form-item label="负责人" name="responsiblePersonName">
  129. <a-input v-model:value="form.responsiblePersonName" allow-clear />
  130. </a-form-item>
  131. </a-col>
  132. <a-col :span="12">
  133. <a-form-item label="负责人电话" name="responsiblePersonPhone">
  134. <a-input v-model:value="form.responsiblePersonPhone" allow-clear />
  135. </a-form-item>
  136. </a-col>
  137. </a-row>
  138. </a-form>
  139. </a-modal>
  140. </div>
  141. </template>
  142. <script setup name="ConfigStation">
  143. import { computed, reactive, ref } from 'vue';
  144. import { message, Modal } from 'ant-design-vue';
  145. import BearJiaUtil from '@/utils/BearJiaUtil';
  146. import { addStation, delStation, getStation, listStation, updateStation } from '@/api/config/station';
  147. import TableActionBar from '@/components/TableActionBar/index.vue';
  148. import TableToolbar from '@/components/BearJiaProTable/TableToolbar.vue';
  149. import { useTableConfigStore } from '@/stores/tableConfig';
  150. import { BearJiaIcon } from '@/utils/BearJiaIcon.js';
  151. const loading = ref(false);
  152. const rows = ref([]);
  153. const formOpen = ref(false);
  154. const formRef = ref();
  155. const selectedRowKeys = ref([]);
  156. const tableCardRef = ref();
  157. const selectedColumnKeys = ref([]);
  158. const tableConfigStore = useTableConfigStore();
  159. const queryParams = reactive({
  160. pageNum: 1,
  161. pageSize: 10,
  162. stationName: undefined,
  163. stationCode: undefined,
  164. stationType: undefined,
  165. isEnable: undefined
  166. });
  167. const pagination = reactive({
  168. current: 1,
  169. pageSize: 10,
  170. total: 0,
  171. showSizeChanger: true
  172. });
  173. const form = reactive({});
  174. const rowSelection = computed(() => ({
  175. selectedRowKeys: selectedRowKeys.value,
  176. onChange: (keys) => { selectedRowKeys.value = keys; }
  177. }));
  178. const stationTypeOptions = [
  179. { label: '风电', value: 0 },
  180. { label: '光伏', value: 1 }
  181. ];
  182. const enableOptions = [
  183. { label: '停用', value: '0' },
  184. { label: '启用', value: '1' }
  185. ];
  186. const rules = {
  187. stationName: [{ required: true, message: '请输入场站名称', trigger: 'blur' }]
  188. };
  189. const columns = [
  190. { title: 'ID', dataIndex: 'id', width: 90 },
  191. { title: '场站名称', dataIndex: 'stationName', width: 180 },
  192. { title: '场站编码', dataIndex: 'stationCode', width: 180 },
  193. { title: '类型', dataIndex: 'stationType', key: 'stationType', width: 100 },
  194. { title: '装机容量(kW)', dataIndex: 'installedCapacity', width: 140 },
  195. { title: '并网容量(kW)', dataIndex: 'gridConnectedCapacity', width: 140 },
  196. { title: '位置', dataIndex: 'position', width: 180 },
  197. { title: '负责人', dataIndex: 'responsiblePersonName', width: 120 },
  198. { title: '电话', dataIndex: 'responsiblePersonPhone', width: 140 },
  199. { title: '启用', dataIndex: 'isEnable', key: 'isEnable', width: 90 },
  200. { title: '操作', key: 'operation', dataIndex: 'operation', width: 200, fixed: 'right' }
  201. ];
  202. const storageKey = 'station-config-columns-station';
  203. const readSavedColumns = () => {
  204. try {
  205. const saved = JSON.parse(localStorage.getItem(storageKey) || 'null');
  206. if (Array.isArray(saved) && saved.length > 0) {
  207. const valid = saved.filter((k) => columns.some((c) => c.dataIndex === k));
  208. if (valid.length > 0) return valid;
  209. }
  210. } catch (e) { /* 忽略存储解析错误 */ }
  211. return columns.map((c) => c.dataIndex);
  212. };
  213. selectedColumnKeys.value = readSavedColumns();
  214. const displayColumns = computed(() => columns.filter((col) => selectedColumnKeys.value.includes(col.dataIndex)));
  215. const exportConfig = { enabled: true };
  216. const stationTypeText = (value) => stationTypeOptions.find((item) => item.value === Number(value))?.label || '-';
  217. const enableText = (value) => enableOptions.find((item) => item.value === String(value))?.label || '-';
  218. const resetObject = (target, source = {}) => {
  219. Object.keys(target).forEach((key) => delete target[key]);
  220. Object.assign(target, source);
  221. };
  222. const handleQuery = async () => {
  223. loading.value = true;
  224. try {
  225. const res = await listStation(queryParams);
  226. rows.value = res?.rows || res?.data || [];
  227. pagination.total = res?.total || rows.value.length;
  228. pagination.current = queryParams.pageNum;
  229. pagination.pageSize = queryParams.pageSize;
  230. } finally {
  231. loading.value = false;
  232. }
  233. };
  234. const resetQuery = () => {
  235. queryParams.pageNum = 1;
  236. queryParams.stationName = undefined;
  237. queryParams.stationCode = undefined;
  238. queryParams.stationType = undefined;
  239. queryParams.isEnable = undefined;
  240. handleQuery();
  241. };
  242. const handleTableChange = (page) => {
  243. queryParams.pageNum = page.current;
  244. queryParams.pageSize = page.pageSize;
  245. handleQuery();
  246. };
  247. const openForm = async (record) => {
  248. if (record?.id) {
  249. const res = await getStation(record.id);
  250. const data = { ...res.data };
  251. if (data.stationType !== undefined && data.stationType !== null) {
  252. data.stationType = Number(data.stationType);
  253. }
  254. resetObject(form, data);
  255. } else {
  256. resetObject(form, { isEnable: '1', stationType: 0 });
  257. }
  258. formOpen.value = true;
  259. };
  260. const submitForm = async () => {
  261. await formRef.value?.validate();
  262. if (form.id) {
  263. await updateStation(form);
  264. } else {
  265. await addStation(form);
  266. }
  267. message.success('保存成功');
  268. formOpen.value = false;
  269. handleQuery();
  270. };
  271. const handleDeleteRecord = (record) => {
  272. Modal.confirm({
  273. title: '确认删除',
  274. content: '确认删除该场站?',
  275. onOk: async () => {
  276. await delStation(record.id);
  277. message.success('删除成功');
  278. handleQuery();
  279. }
  280. });
  281. };
  282. const handleBatchDelete = () => {
  283. if (selectedRowKeys.value.length <= 0) {
  284. message.warning('请至少选择一条数据');
  285. return;
  286. }
  287. Modal.confirm({
  288. title: '确认删除',
  289. content: `确认删除选中的 ${selectedRowKeys.value.length} 条数据?`,
  290. onOk: async () => {
  291. await delStation(selectedRowKeys.value);
  292. message.success('删除成功');
  293. selectedRowKeys.value = [];
  294. handleQuery();
  295. }
  296. });
  297. };
  298. const handleExport = () => {
  299. BearJiaUtil.download('config/station/export', queryParams, '场站信息.xlsx');
  300. };
  301. const handleColumnSettingsChange = (keys) => {
  302. selectedColumnKeys.value = keys;
  303. localStorage.setItem(storageKey, JSON.stringify(keys));
  304. };
  305. const handleFullscreen = (isFullscreen) => {
  306. const el = tableCardRef.value?.$el || tableCardRef.value;
  307. if (isFullscreen) {
  308. el?.requestFullscreen?.();
  309. } else {
  310. document.exitFullscreen?.();
  311. }
  312. };
  313. const handleTableReset = () => {
  314. selectedColumnKeys.value = columns.map((c) => c.dataIndex);
  315. localStorage.removeItem(storageKey);
  316. handleQuery();
  317. };
  318. handleQuery();
  319. </script>
  320. <style lang="less" scoped>
  321. .station-page {
  322. .section {
  323. margin-top: 12px;
  324. }
  325. .query-select {
  326. width: 140px;
  327. }
  328. .full-input {
  329. width: 100%;
  330. }
  331. }
  332. </style>