index.vue 15 KB

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