index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <template>
  2. <div class="h-full flex flex-col gap-y-3">
  3. <n-page-header @back="$router.back">
  4. <template #title> 数据编辑 </template>
  5. </n-page-header>
  6. <div class="flex-1 grid grid-cols-4 gap-x-3">
  7. <n-card>
  8. <n-tabs display-directive="show">
  9. <n-tab-pane name="1" tab="设备">
  10. <n-list>
  11. <template #header>
  12. <n-checkbox v-model:checked="checked" @update:checked="handleSelectAll">
  13. 全选
  14. </n-checkbox>
  15. </template>
  16. <n-scrollbar :style="{ maxHeight: `${height - 310}px` }" trigger="none">
  17. <n-checkbox-group v-model:value="checkValues" @update:value="handleCheckValues">
  18. <template v-for="item of classList" :key="item.T_id">
  19. <n-list-item class="mr-5">
  20. <template #prefix>
  21. <n-checkbox :value="item.T_id" />
  22. </template>
  23. <template #suffix>
  24. <n-space :wrap="false">
  25. <EditClass :task="task" :taskClass="item" :getClassList="getClassList" />
  26. <DeleteClass :task="task" :taskClass="item" :getClassList="getClassList" />
  27. </n-space>
  28. </template>
  29. <n-thing>
  30. <template #header> {{ item.T_id }} </template>
  31. <template #description> {{ item.T_sn }} </template>
  32. </n-thing>
  33. </n-list-item>
  34. </template>
  35. </n-checkbox-group>
  36. </n-scrollbar>
  37. <template #footer>
  38. <n-gradient-text type="info">
  39. 传感器总数:{{ classList.length }}
  40. </n-gradient-text>
  41. </template>
  42. </n-list>
  43. </n-tab-pane>
  44. <n-tab-pane name="2" tab="表单">
  45. <FormList :task="task" :class-list="classList" :time="time" :temporal-interval="temporalInterval" />
  46. </n-tab-pane>
  47. </n-tabs>
  48. </n-card>
  49. <n-card class="h-full col-span-3">
  50. <div class="h-full flex flex-col gap-y-3">
  51. <n-space justify="space-between">
  52. <n-input-group>
  53. <n-date-picker format="yyyy-MM-dd HH:mm" :time-picker-props="{ format: 'HH:mm' }" @update:formatted-value="(value) => {
  54. queryData.Time_start = value[0];
  55. queryData.Time_end = value[1];
  56. }
  57. " type="datetimerange" clearable />
  58. <n-button type="primary" @click="getDataList">搜索</n-button>
  59. </n-input-group>
  60. <n-space>
  61. <ExportVue :task="task" :class-list="classList" />
  62. <ImportVue :task="task" />
  63. <ImportPlatform :task="task" />
  64. <AddVue :class-list="classList" :task="task" />
  65. <SetVue @submit="handleSet" />
  66. </n-space>
  67. </n-space>
  68. <n-tabs type="segment" animated v-model:value="tabChart" @update:value="handleTabChange">
  69. <n-tab-pane name="温度" tab="温度">
  70. <Chart :style="{ minHeight: `${height - 300}px` }" ref="chart1" constructor-type="stockChart"
  71. :options="chartOptions1"></Chart>
  72. </n-tab-pane>
  73. <n-tab-pane name="湿度" tab="湿度">
  74. <Chart :style="{ minHeight: `${height - 300}px` }" ref="chart2" constructor-type="stockChart"
  75. :options="chartOptions2"></Chart>
  76. </n-tab-pane>
  77. </n-tabs>
  78. </div>
  79. </n-card>
  80. </div>
  81. </div>
  82. <n-modal v-model:show="modal.showModal" :title="modal.title" :show-icon="false" preset="dialog">
  83. <template v-if="modal.title === '温度'">
  84. <n-form-item show-require-mark>
  85. <n-input v-model:value="formValue.T_t" />
  86. </n-form-item>
  87. </template>
  88. <template v-else>
  89. <n-form-item show-require-mark>
  90. <n-input v-model:value="formValue.T_rh" />
  91. </n-form-item>
  92. </template>
  93. <n-space justify="end">
  94. <n-popconfirm @positive-click="deleteTask">
  95. <template #trigger>
  96. <n-button type="error">删除该点</n-button>
  97. </template>
  98. 是否确认删除?
  99. </n-popconfirm>
  100. <n-button type="primary" @click="editTask">确定</n-button>
  101. </n-space>
  102. </n-modal>
  103. </template>
  104. <script setup>
  105. import { Chart } from 'highcharts-vue';
  106. import {
  107. deleteTaskData,
  108. editTaskData,
  109. getTaskDataClassList,
  110. getTaskDataList,
  111. } from '@/api';
  112. import AddVue from './AddVue.vue';
  113. import ImportVue from './ImportVue.vue';
  114. import ImportPlatform from './ImportPlatform.vue';
  115. import SetVue from './SetVue.vue';
  116. import ExportVue from './ExportVue.vue';
  117. import FormList from './FormList.vue';
  118. // import ForData from './formData.vue';
  119. import { useWindowSize } from '@vueuse/core';
  120. import { useNow, useDateFormat } from '@vueuse/core';
  121. import EditClass from './EditTaskClass.vue';
  122. import DeleteClass from './DeleteTaskClass.vue';
  123. import { dateFormat } from 'highcharts';
  124. const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss');
  125. const notification = useNotification();
  126. const { height } = useWindowSize();
  127. const message = useMessage();
  128. const task = window.sessionStorage.getItem('task')
  129. ? JSON.parse(window.sessionStorage.getItem('task'))
  130. : {};
  131. const chart1 = ref(null);
  132. const chart2 = ref(null);
  133. // Modal 数据源
  134. const modal = reactive({
  135. showModal: false,
  136. title: '',
  137. });
  138. // 查询数据
  139. const queryData = reactive({
  140. T_task_id: task.T_task_id,
  141. T_sn: '',
  142. T_id: '',
  143. Time_start: '',
  144. Time_end: '',
  145. page: 1,
  146. page_z: 1000,
  147. });
  148. // 表单数据
  149. const formValue = reactive({
  150. T_t: null,
  151. T_rh: null,
  152. });
  153. // 数据源对象
  154. // const state = reactive({});
  155. // 设备列表
  156. const classList = ref([]);
  157. // 列表
  158. const dataList = ref([]);
  159. //
  160. const dataInfo = ref({});
  161. // 选项组受控模式下的值
  162. const checkValues = ref(null);
  163. // tab
  164. const tabChart = ref('温度');
  165. const handleTabChange = (value) => {
  166. console.log(value);
  167. checkValues.value = [];
  168. checked.value = false;
  169. };
  170. //
  171. const handleSet = (data) => {
  172. if (tabChart.value === '温度') {
  173. chart1.value.chart.yAxis[0].addPlotLine({
  174. color: 'red',
  175. width: 2,
  176. value: data.tTop,
  177. label: {
  178. text: `上限(${data.tTop})`,
  179. },
  180. });
  181. chart1.value.chart.yAxis[0].addPlotLine({
  182. color: 'red',
  183. width: 2,
  184. value: data.tBottom,
  185. label: {
  186. text: `下限(${data.tBottom})`,
  187. },
  188. });
  189. } else {
  190. chart2.value.chart.yAxis[0].addPlotLine({
  191. color: 'red',
  192. width: 2,
  193. value: data.hTop,
  194. label: {
  195. text: `上限(${data.hTop})`,
  196. },
  197. });
  198. chart2.value.chart.yAxis[0].addPlotLine({
  199. color: 'red',
  200. width: 2,
  201. value: data.hBottom,
  202. label: {
  203. text: `下限(${data.hBottom})`,
  204. },
  205. });
  206. }
  207. };
  208. const checked = ref(false);
  209. // 全选
  210. const handleSelectAll = async (value) => {
  211. if (value) {
  212. checkValues.value = classList.value.map((item) => item.T_id);
  213. const arr = classList.value.filter(
  214. (item) => !selectedValue.value.includes(item.T_id)
  215. );
  216. for (let item of arr) {
  217. queryData.T_id = item.T_id;
  218. queryData.T_sn = item.T_sn;
  219. await getDataList();
  220. if (tabChart.value === '温度') {
  221. const data1 = dataList.value
  222. .map((item) => [new Date(item.T_time).getTime(), item.T_t])
  223. .sort((a, b) => a[0] - b[0]);
  224. chart1.value.chart.addSeries({
  225. id: item.T_id,
  226. name: item.T_id,
  227. data: data1,
  228. lineWidth: 1,
  229. cursor: 'pointer',
  230. events: {
  231. click(e) {
  232. modal.title = '温度';
  233. modal.showModal = true;
  234. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  235. formValue.T_t = e.point.y;
  236. queryData.T_id = e.point.series.name;
  237. getDataList();
  238. dataInfo.value = dataList.value[e.point.index];
  239. },
  240. },
  241. });
  242. } else {
  243. const data2 = dataList.value
  244. .map((item) => [new Date(item.T_time).getTime(), item.T_rh])
  245. .sort((a, b) => a[0] - b[0]);
  246. chart2.value.chart.addSeries({
  247. id: item.T_id,
  248. name: item.T_id,
  249. data: data2,
  250. lineWidth: 1,
  251. cursor: 'pointer',
  252. events: {
  253. click(e) {
  254. console.log('////',e)
  255. modal.title = '湿度';
  256. modal.showModal = true;
  257. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  258. formValue.T_rh = e.point.y;
  259. queryData.T_id = e.point.series.name;
  260. getDataList();
  261. dataInfo.value = dataList.value[e.point.index];
  262. },
  263. },
  264. });
  265. }
  266. }
  267. } else {
  268. if (tabChart.value === '温度') {
  269. classList.value.forEach((item) => {
  270. chart1.value.chart.get(item.T_id).remove();
  271. });
  272. } else {
  273. classList.value.forEach((item) => {
  274. chart2.value.chart.get(item.T_id).remove();
  275. });
  276. }
  277. checkValues.value = [];
  278. }
  279. };
  280. const time = ref('');
  281. const selectedValue = ref([]);
  282. // 选项组的值改变时的回调
  283. const handleCheckValues = async (values, meta) => {
  284. selectedValue.value = values;
  285. const classInfo = classList.value.find((item) => item.T_id === meta.value);
  286. queryData.T_id = classInfo.T_id;
  287. queryData.T_sn = classInfo.T_sn;
  288. await getDataList();
  289. const data1 = dataList.value
  290. .map((item) => [new Date(item.T_time).getTime(), item.T_t])
  291. .sort((a, b) => a[0] - b[0]);
  292. const data2 = dataList.value
  293. .map((item) => [new Date(item.T_time).getTime(), item.T_rh])
  294. .sort((a, b) => a[0] - b[0]);
  295. if (meta.actionType === 'check') {
  296. if (tabChart.value === '温度') {
  297. chart1.value.chart.addSeries({
  298. id: meta.value,
  299. name: meta.value,
  300. data: data1,
  301. cursor: 'pointer',
  302. events: {
  303. click(e) {
  304. console.log('/=======================///',e.point.x)
  305. modal.title = '温度';
  306. modal.showModal = true;
  307. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  308. formValue.T_t = e.point.y;
  309. queryData.T_id = e.point.series.name;
  310. getDataList();
  311. dataInfo.value = dataList.value[e.point.index];
  312. },
  313. },
  314. });
  315. } else {
  316. chart2.value.chart.addSeries({
  317. id: meta.value,
  318. name: meta.value,
  319. data: data2,
  320. events: {
  321. click(e) {
  322. console.log('点击11119',e)
  323. modal.title = '湿度';
  324. modal.showModal = true;
  325. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  326. formValue.T_rh = e.point.y;
  327. queryData.T_id = e.point.series.name;
  328. getDataList();
  329. dataInfo.value = dataList.value[e.point.index];
  330. },
  331. },
  332. });
  333. }
  334. } else {
  335. if (tabChart.value === '温度') {
  336. chart1.value.chart.get(meta.value).remove();
  337. } else {
  338. chart2.value.chart.get(meta.value).remove();
  339. }
  340. }
  341. };
  342. const temporalInterval = ref('');
  343. // 图表配置
  344. const chartOptions1 = {
  345. xAxis: {
  346. labels: {
  347. format: '{value:%Y-%m-%d %H:%M:%S}',
  348. },
  349. },
  350. yAxis: {
  351. labels: {
  352. format: '{text}℃',
  353. },
  354. plotLines: [],
  355. },
  356. legend: {
  357. enabled: true,
  358. },
  359. accessibility: {
  360. enabled: false,
  361. },
  362. boost: {
  363. useGPUTranslations: true,
  364. seriesThreshold: 5,
  365. },
  366. scrollbar: {
  367. enabled: false,
  368. },
  369. // tooltip: {
  370. // formatter() {
  371. // return `${this.y} ${dateFormat('%Y-%m-%d %H:%M:%S', this.x)}`;
  372. // },
  373. // },
  374. chart: {
  375. zooming: {
  376. singleTouch: true,
  377. resetButton: {},
  378. type: 'xy',
  379. },
  380. events: {
  381. selection(event) {
  382. // console.log(
  383. // dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].min),
  384. // dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].max)
  385. // );
  386. if (event.xAxis) {
  387. console.log('时间区间选择', event.xAxis)
  388. temporalInterval.value = [
  389. dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].min),
  390. dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].max),
  391. ];
  392. }
  393. },
  394. },
  395. },
  396. series: [],
  397. };
  398. const chartOptions2 = {
  399. xAxis: {
  400. labels: {
  401. format: '{value:%Y-%m-%d %H:%M:%S}',
  402. },
  403. },
  404. yAxis: {
  405. labels: {
  406. format: '{text}%',
  407. },
  408. },
  409. legend: {
  410. enabled: true,
  411. },
  412. accessibility: {
  413. enabled: false,
  414. },
  415. boost: {
  416. useGPUTranslations: true,
  417. seriesThreshold: 5,
  418. },
  419. scrollbar: {
  420. enabled: false,
  421. },
  422. chart: {
  423. zoomType: 'xy',
  424. },
  425. series: [],
  426. };
  427. // 删除
  428. const deleteTask = async () => {
  429. const { data: res } = await deleteTaskData({
  430. T_task_id: queryData.T_task_id,
  431. Id: dataInfo.value.ID,
  432. });
  433. if (res.Code === 200) {
  434. modal.showModal = false;
  435. message.success(res.Msg);
  436. }
  437. };
  438. // 编辑
  439. const editTask = async () => {
  440. const { data: res } = await editTaskData({
  441. T_task_id: queryData.T_task_id,
  442. Id: dataInfo.value.ID,
  443. T_t: formValue.T_t ? formValue.T_t : dataInfo.value.T_t,
  444. T_rh: formValue.T_rh ? formValue.T_rh : dataInfo.value.T_rh,
  445. T_time: dataInfo.value.T_time,
  446. });
  447. if (res.Code === 200) {
  448. modal.showModal = false;
  449. message.success(res.Msg);
  450. }
  451. };
  452. // 获取设备列表
  453. const getClassList = async () => {
  454. const { data: res } = await getTaskDataClassList({
  455. T_task_id: queryData.T_task_id,
  456. });
  457. classList.value = res.Data || [];
  458. };
  459. // 获取任务数据列表
  460. const getDataList = async () => {
  461. const { data: res } = await getTaskDataList(queryData);
  462. if (queryData.page_z <= res.Data.Page_size) {
  463. const arr = classList.value.filter((item) =>
  464. checkValues.value.includes(item.T_sn)
  465. );
  466. arr.forEach((item) => {
  467. item.T_sn = queryData.T_sn;
  468. item.T_id = queryData.T_id;
  469. getDataList();
  470. });
  471. }
  472. dataList.value = res.Data.List || [];
  473. };
  474. getClassList();
  475. onMounted(() => {
  476. if (task.T_collection_state === 0) {
  477. notification.info({
  478. closable: false,
  479. title: '未完成',
  480. meta: `当前时间:${formatted.value}`,
  481. duration: 2500,
  482. keepAliveOnHover: true,
  483. });
  484. } else if (task.T_collection_state === 1) {
  485. notification.info({
  486. closable: false,
  487. title: '已完成',
  488. meta: `当前时间:${formatted.value}`,
  489. duration: 2500,
  490. keepAliveOnHover: true,
  491. });
  492. } else if (task.T_collection_state === 2) {
  493. notification.info({
  494. closable: false,
  495. title: '处理中',
  496. meta: `当前时间:${formatted.value}`,
  497. duration: 2500,
  498. keepAliveOnHover: true,
  499. });
  500. } else if (task.T_collection_state === 3) {
  501. notification.info({
  502. closable: false,
  503. title: '已采集-无数据',
  504. meta: `当前时间:${formatted.value}`,
  505. duration: 2500,
  506. keepAliveOnHover: true,
  507. });
  508. }
  509. });
  510. </script>
  511. <style lang="scss" scoped></style>