index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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. import { TimeData } from '@/plugin/timeFun';
  125. const formatted = useDateFormat(useNow(), 'YYYY-MM-DD HH:mm:ss');
  126. const notification = useNotification();
  127. const { height } = useWindowSize();
  128. const message = useMessage();
  129. const task = window.sessionStorage.getItem('task')
  130. ? JSON.parse(window.sessionStorage.getItem('task'))
  131. : {};
  132. const chart1 = ref(null);
  133. const chart2 = ref(null);
  134. // Modal 数据源
  135. const modal = reactive({
  136. showModal: false,
  137. title: '',
  138. });
  139. // 查询数据
  140. const queryData = reactive({
  141. T_task_id: task.T_task_id,
  142. T_sn: '',
  143. T_id: '',
  144. Time_start: '2023-02-06 21:00',
  145. Time_end: '2023-02-06 21:10',
  146. page: 1,
  147. page_z: 9999999,
  148. });
  149. // 表单数据
  150. const formValue = reactive({
  151. T_t: null,
  152. T_rh: null,
  153. });
  154. // 数据源对象
  155. // const state = reactive({});
  156. // 设备列表
  157. const classList = ref([]);
  158. // 列表
  159. const dataList = ref([]);
  160. //
  161. const dataInfo = ref({});
  162. // 选项组受控模式下的值
  163. const checkValues = ref(null);
  164. // tab
  165. const tabChart = ref('温度');
  166. const handleTabChange = (value) => {
  167. console.log(value);
  168. checkValues.value = [];
  169. checked.value = false;
  170. };
  171. //
  172. const handleSet = (data) => {
  173. if (tabChart.value === '温度') {
  174. chart1.value.chart.yAxis[0].addPlotLine({
  175. color: 'red',
  176. width: 2,
  177. value: data.tTop,
  178. label: {
  179. text: `上限(${data.tTop})`,
  180. },
  181. });
  182. chart1.value.chart.yAxis[0].addPlotLine({
  183. color: 'red',
  184. width: 2,
  185. value: data.tBottom,
  186. label: {
  187. text: `下限(${data.tBottom})`,
  188. },
  189. });
  190. } else {
  191. chart2.value.chart.yAxis[0].addPlotLine({
  192. color: 'red',
  193. width: 2,
  194. value: data.hTop,
  195. label: {
  196. text: `上限(${data.hTop})`,
  197. },
  198. });
  199. chart2.value.chart.yAxis[0].addPlotLine({
  200. color: 'red',
  201. width: 2,
  202. value: data.hBottom,
  203. label: {
  204. text: `下限(${data.hBottom})`,
  205. },
  206. });
  207. }
  208. };
  209. const checked = ref(false);
  210. // 全选
  211. const handleSelectAll = async (value) => {
  212. if (value) {
  213. checkValues.value = classList.value.map((item) => item.T_id);
  214. const arr = classList.value.filter(
  215. (item) => !selectedValue.value.includes(item.T_id)
  216. );
  217. for (let item of arr) {
  218. queryData.T_id = item.T_id;
  219. queryData.T_sn = item.T_sn;
  220. await getDataList();
  221. if (tabChart.value === '温度') {
  222. const data1 = dataList.value
  223. .map((item) => [new Date(item.T_time).getTime(), item.T_t])
  224. .sort((a, b) => a[0] - b[0]);
  225. chart1.value.chart.addSeries({
  226. id: item.T_id,
  227. name: item.T_id,
  228. data: data1,
  229. lineWidth: 1,
  230. cursor: 'pointer',
  231. events: {
  232. click(e) {
  233. modal.title = '温度';
  234. modal.showModal = true;
  235. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  236. formValue.T_t = e.point.y;
  237. queryData.T_id = e.point.series.name;
  238. getDataList();
  239. dataInfo.value = dataList.value[e.point.index];
  240. },
  241. },
  242. });
  243. } else {
  244. const data2 = dataList.value
  245. .map((item) => [new Date(item.T_time).getTime(), item.T_rh])
  246. .sort((a, b) => a[0] - b[0]);
  247. chart2.value.chart.addSeries({
  248. id: item.T_id,
  249. name: item.T_id,
  250. data: data2,
  251. lineWidth: 1,
  252. cursor: 'pointer',
  253. events: {
  254. click(e) {
  255. console.log('////',e)
  256. modal.title = '湿度';
  257. modal.showModal = true;
  258. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  259. formValue.T_rh = e.point.y;
  260. queryData.T_id = e.point.series.name;
  261. getDataList();
  262. dataInfo.value = dataList.value[e.point.index];
  263. },
  264. },
  265. });
  266. }
  267. }
  268. } else {
  269. if (tabChart.value === '温度') {
  270. classList.value.forEach((item) => {
  271. chart1.value.chart.get(item.T_id).remove();
  272. });
  273. } else {
  274. classList.value.forEach((item) => {
  275. chart2.value.chart.get(item.T_id).remove();
  276. });
  277. }
  278. checkValues.value = [];
  279. }
  280. };
  281. const time = ref('');
  282. const selectedValue = ref([]);
  283. // 选项组的值改变时的回调
  284. const handleCheckValues = async (values, meta) => {
  285. selectedValue.value = values;
  286. const classInfo = classList.value.find((item) => item.T_id === meta.value);
  287. queryData.T_id = classInfo.T_id;
  288. queryData.T_sn = classInfo.T_sn;
  289. await getDataList();
  290. const data1 = dataList.value
  291. .map((item) => [new Date(item.T_time).getTime(), item.T_t])
  292. .sort((a, b) => a[0] - b[0]);
  293. const data2 = dataList.value
  294. .map((item) => [new Date(item.T_time).getTime(), item.T_rh])
  295. .sort((a, b) => a[0] - b[0]);
  296. if (meta.actionType === 'check') {
  297. if (tabChart.value === '温度') {
  298. chart1.value.chart.addSeries({
  299. id: meta.value,
  300. name: meta.value,
  301. data: data1,
  302. cursor: 'pointer',
  303. events: {
  304. click(e) {
  305. console.log('/=======================///',e,TimeData(e.point.x))
  306. modal.title = '温度';
  307. modal.showModal = true;
  308. time.value = TimeData(e.point.x);
  309. formValue.T_t = e.point.y;
  310. queryData.T_id = e.point.series.name;
  311. getDataList();
  312. dataInfo.value = dataList.value[e.point.index];
  313. },
  314. },
  315. });
  316. } else {
  317. chart2.value.chart.addSeries({
  318. id: meta.value,
  319. name: meta.value,
  320. data: data2,
  321. events: {
  322. click(e) {
  323. console.log('点击11119',e)
  324. modal.title = '湿度';
  325. modal.showModal = true;
  326. time.value = dateFormat('%Y-%m-%d %H:%M:%S', e.point.x);
  327. formValue.T_rh = e.point.y;
  328. queryData.T_id = e.point.series.name;
  329. getDataList();
  330. dataInfo.value = dataList.value[e.point.index];
  331. },
  332. },
  333. });
  334. }
  335. } else {
  336. if (tabChart.value === '温度') {
  337. chart1.value.chart.get(meta.value).remove();
  338. } else {
  339. chart2.value.chart.get(meta.value).remove();
  340. }
  341. }
  342. };
  343. const temporalInterval = ref('');
  344. // 图表配置
  345. const chartOptions1 = {
  346. xAxis: {
  347. labels: {
  348. format: '{value:%Y-%m-%d %H:%M:%S}',
  349. },
  350. },
  351. time: {
  352. useUTC: false
  353. },
  354. boost: {
  355. useGPUTranslations: true
  356. },
  357. tooltip: {
  358. xDateFormat: '%Y-%m-%d %H:%M:%S',
  359. // headerFormat: '<small class="headerFormat">{.key}:{point.stackTotal}</small><table>',
  360. // valueDecimals: 0 //会导致提示框内容显示错误
  361. },
  362. yAxis: {
  363. labels: {
  364. format: '{text}℃',
  365. },
  366. plotLines: [],
  367. },
  368. legend: {
  369. enabled: true,
  370. },
  371. accessibility: {
  372. enabled: false,
  373. },
  374. boost: {
  375. useGPUTranslations: true,
  376. seriesThreshold: 5,
  377. },
  378. scrollbar: {
  379. enabled: false,
  380. },
  381. // tooltip: {
  382. // formatter() {
  383. // return `${this.y} ${dateFormat('%Y-%m-%d %H:%M:%S', this.x)}`;
  384. // },
  385. // },
  386. chart: {
  387. zooming: {
  388. singleTouch: true,
  389. resetButton: {},
  390. type: 'xy',
  391. },
  392. events: {
  393. selection(event) {
  394. // console.log(
  395. // dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].min),
  396. // dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].max)
  397. // );
  398. if (event.xAxis) {
  399. console.log('时间区间选择', event.xAxis)
  400. temporalInterval.value = [
  401. dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].min),
  402. dateFormat('%Y-%m-%d %H:%M', event.xAxis[0].max),
  403. ];
  404. }
  405. },
  406. },
  407. },
  408. series: [],
  409. };
  410. const chartOptions2 = {
  411. xAxis: {
  412. labels: {
  413. format: '{value:%Y-%m-%d %H:%M:%S}',
  414. },
  415. },
  416. yAxis: {
  417. labels: {
  418. format: '{text}%',
  419. },
  420. },
  421. legend: {
  422. enabled: true,
  423. },
  424. accessibility: {
  425. enabled: false,
  426. },
  427. boost: {
  428. useGPUTranslations: true,
  429. seriesThreshold: 5,
  430. },
  431. scrollbar: {
  432. enabled: false,
  433. },
  434. chart: {
  435. zoomType: 'xy',
  436. },
  437. series: [],
  438. };
  439. // 删除
  440. const deleteTask = async () => {
  441. const { data: res } = await deleteTaskData({
  442. T_task_id: queryData.T_task_id,
  443. Id: dataInfo.value.ID,
  444. });
  445. if (res.Code === 200) {
  446. modal.showModal = false;
  447. message.success(res.Msg);
  448. }
  449. };
  450. // 编辑
  451. const editTask = async () => {
  452. console.log('提交',dataInfo,time.value)
  453. const { data: res } = await editTaskData({
  454. T_task_id: queryData.T_task_id,
  455. Id: dataInfo.value.ID,
  456. T_t: formValue.T_t ? formValue.T_t : dataInfo.value.T_t,
  457. T_rh: formValue.T_rh ? formValue.T_rh : dataInfo.value.T_rh,
  458. T_time: time.value,
  459. });
  460. if (res.Code === 200) {
  461. modal.showModal = false;
  462. message.success(res.Msg);
  463. }
  464. };
  465. // 获取设备列表
  466. const getClassList = async () => {
  467. const { data: res } = await getTaskDataClassList({
  468. T_task_id: queryData.T_task_id,
  469. });
  470. classList.value = res.Data || [];
  471. };
  472. // 获取任务数据列表
  473. const getDataList = async () => {
  474. const { data: res } = await getTaskDataList(queryData);
  475. if (queryData.page_z <= res.Data.Page_size) {
  476. const arr = classList.value.filter((item) =>
  477. checkValues.value.includes(item.T_sn)
  478. );
  479. arr.forEach((item) => {
  480. item.T_sn = queryData.T_sn;
  481. item.T_id = queryData.T_id;
  482. getDataList();
  483. });
  484. }
  485. dataList.value = res.Data.List || [];
  486. };
  487. getClassList();
  488. onMounted(() => {
  489. if (task.T_collection_state === 0) {
  490. notification.info({
  491. closable: false,
  492. title: '未完成',
  493. meta: `当前时间:${formatted.value}`,
  494. duration: 2500,
  495. keepAliveOnHover: true,
  496. });
  497. } else if (task.T_collection_state === 1) {
  498. notification.info({
  499. closable: false,
  500. title: '已完成',
  501. meta: `当前时间:${formatted.value}`,
  502. duration: 2500,
  503. keepAliveOnHover: true,
  504. });
  505. } else if (task.T_collection_state === 2) {
  506. notification.info({
  507. closable: false,
  508. title: '处理中',
  509. meta: `当前时间:${formatted.value}`,
  510. duration: 2500,
  511. keepAliveOnHover: true,
  512. });
  513. } else if (task.T_collection_state === 3) {
  514. notification.info({
  515. closable: false,
  516. title: '已采集-无数据',
  517. meta: `当前时间:${formatted.value}`,
  518. duration: 2500,
  519. keepAliveOnHover: true,
  520. });
  521. }
  522. });
  523. </script>
  524. <style lang="scss" scoped></style>