HistoryTable.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <template>
  2. <div class="history-table">
  3. <BasicTable ref="historyTable" @register="register" :data-source="data" :scroll="scroll" @change="search">
  4. <template #bodyCell="{ column, record }">
  5. <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == '0' ? 'green' : 'red'">
  6. {{ record.warnFlag == '0' ? '正常' : '报警' }}
  7. </a-tag>
  8. <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == '0' ? '#f00' : 'green'">
  9. {{ record.netStatus == '0' ? '断开' : '连接' }}
  10. </a-tag>
  11. </template>
  12. <template #form-submitBefore>
  13. <a-button type="primary" preIcon="ant-design:search-outlined" @click="search">查询</a-button>
  14. </template>
  15. </BasicTable>
  16. </div>
  17. </template>
  18. <script lang="ts" setup>
  19. // 场景类历史数据公共组件!
  20. // 用于服务场景类历史数据业务,这类数据通常由一条数据返回多个子设备的信息,例如:{ forcFan1Temp, forcFan2Temp };
  21. // 而此组件可以将这些数据分类,例如:表格只有 温度 一列,但可以根据所选的子设备展示不同的数据;
  22. // 综上所述,此组件在基础的历史数据组件上添加了子设备下拉框(由字典驱动),用户选择子设备后表头将动态调整以适配数据展示;
  23. //
  24. // 使用方法如下:
  25. // 设有历史数据2条,[{ name, forcFan1Temp, forcFan2Temp }, { name, forcFan1Temp, forcFan2Temp }]。
  26. //
  27. // 1、配置设备字段(参考公司端综合设备管理-设备字段管理)
  28. // 以压风机为例,设压风机设备的历史数据编码为forcFan_history。
  29. // 那么字段code中需要把所有字段悉数配置,例子如下:
  30. // 显示字段 字段code
  31. // 温度 forcFanTemp
  32. // 安装位置 name
  33. //
  34. // 2、配置数据字典(参考系统管理-数据字典),为上述设备配置子设备
  35. // 同以压风机为例,设压风机子设备字典编码为forcFan_dict,且已经新增到系统中。
  36. // 则字典配置的例子如下:
  37. // 名称 数据值
  38. // 压风机1 forcFan1
  39. // 压风机2 forcFan2
  40. //
  41. // 3、运维人员应配合前端开发人员,使用指定的编码配置内容。
  42. // 同以压风机为例,需使用device-code(forcFan)、dict-code(forcFan_dict)。
  43. //
  44. // 4、其他内容说明
  45. // 同以压风机为例,当子设备没有数据时,不进行过滤,此时展示的数据是:
  46. // 温度 安装位置
  47. // 取forcFanTemp 取name
  48. // 同以压风机为例,当子设备选择压风机1时,过滤压风机1相关的表头,此时展示的数据是:
  49. // 温度 安装位置
  50. // 取forcFan1Temp 取name
  51. // 当设备字段不含数据字典关键词(forcFan)时不做处理,当设备字段包含关键词但已指定编号(即字段包含数字)时不做处理
  52. import { onMounted, ref, shallowRef } from 'vue';
  53. import { BasicColumn, PaginationProps, BasicTable } from '/@/components/Table';
  54. import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
  55. import { defaultFormProps, defaultPaginationProps, getDefaultSchemas, defaultTableProps } from './history.data';
  56. import { getDeviceList, list } from './history.api';
  57. import { useListPage } from '/@/hooks/system/useListPage';
  58. import { initDictOptions } from '/@/utils/dict';
  59. const props = withDefaults(
  60. defineProps<{
  61. /** 表格项配置,默认由deviceCode获取且联动dictCode,可以覆写,覆写后将不支持联动,参考BaiscTable */
  62. // columns?: BasicColumn[];
  63. /** 表格操作项配置,默认为空,可以覆写 */
  64. // actionColumns?: BasicColumn;
  65. /** 查询表单项配置,默认联动dictCode,可以覆写,覆写后将不支持联动,提供formProps时此项无效,参考BaiscTable */
  66. // schemas?: FormSchema[];
  67. /** 表格分页配置,可以覆写,参考BaiscTable */
  68. pagination?: PaginationProps;
  69. /** 设备编码,该编码用于请求设备信息,示例:forcFan */
  70. deviceCode: string;
  71. /** 字典编码,该编码用于从字典配置中读出设备项,示例:forcFan_dict */
  72. dictCode: string;
  73. /** 字段编码,该编码用于从设备字段配置中读取默认表头信息,示例:forcFan_history */
  74. columnsCode: string;
  75. scroll: { x: number | true; y: number };
  76. /** 表格配置,参考BaiscTable,该值会与默认的配置进行浅合并,这里提供的任何配置都是优先的 */
  77. // tableProps?: BasicTableProps;
  78. /** 查询表单配置,参考BaiscTable */
  79. // formProps?: FormProps;
  80. }>(),
  81. {
  82. deviceCode: '',
  83. dictCode: '',
  84. }
  85. );
  86. // 创建表格,此表格目前不具备常用功能,需要初始化后使用(props指定表格配置时除外)
  87. let originColumns: BasicColumn[] = [];
  88. const { tableContext } = useListPage({ tableProps: defaultTableProps });
  89. const [register, { getForm, setLoading, getPaginationRef, setPagination, setProps, setColumns }] = tableContext;
  90. /**
  91. * 初始化表格,该方法将根据参数设定新的表头、表单,如果提供了自定义的表头、表单配置则不作操作。
  92. *
  93. * 之所以将设备相关的信息作参数传入,是因为这样可以确认依赖关系,即需要有设备信息之后再初始化表格。
  94. *
  95. * @param deviceCodes 获取表头所用的编码,从左到右依次尝试,直到找到第一个有表头信息的为止
  96. * @param deviceOptions 设备下拉框对应的选项
  97. */
  98. function initTable(deviceCodes: string[], deviceOptions: any[], dictOptions: any[]) {
  99. const defaultSchemas = getDefaultSchemas(dictOptions, deviceOptions);
  100. for (const code of deviceCodes) {
  101. const cols = getTableHeaderColumns(code);
  102. if (cols.length) {
  103. originColumns = cols;
  104. break;
  105. }
  106. }
  107. setProps({
  108. formConfig: {
  109. ...defaultFormProps,
  110. schemas: defaultSchemas,
  111. },
  112. pagination: props.pagination || defaultPaginationProps,
  113. });
  114. }
  115. /**
  116. * 更新表头,表头默认情况下需要和子设备联动
  117. * @param prefix 子设备的值,即为表头取数据时字段的前缀
  118. */
  119. function updateColumns(prefix?: string) {
  120. if (!prefix) return setColumns(originColumns);
  121. // 如果有子设备信息,筛选符合规范的表头
  122. const cols = originColumns.map((col) => {
  123. const dataIndex = col.dataIndex as string;
  124. // 获取到子设备编码的前缀及编码,正则例子:forcFan1 => [forcFan1, forcFan, 1]
  125. const [_, pfx] = prefix.match(/([A-Za-z]+)([0-9]+)/) || [];
  126. // 同时,如若已经在前缀后配置了编号则不要更改
  127. const reg = new RegExp(`${pfx}[0-9]`);
  128. if (dataIndex.search(reg) !== -1) return col;
  129. if (dataIndex.includes(pfx)) {
  130. return {
  131. ...col,
  132. dataIndex: dataIndex.replace(pfx, prefix),
  133. };
  134. }
  135. // 默认直接放行
  136. return col;
  137. });
  138. setColumns(cols);
  139. }
  140. // 表格数据相关的字段
  141. const data = shallowRef([]);
  142. /**
  143. * 获取列表的数据
  144. *
  145. * 这些参数确认了依赖关系,即需要有表单数据、设备信息之后再尝试获取表格数据。
  146. *
  147. * @param formData 表格上方的表单数据
  148. * @param deviceCode 设备编码
  149. * @param deviceInfo 设备信息
  150. */
  151. function fetchData(formData: Record<string, unknown>, deviceCode: string, deviceInfo: any) {
  152. setLoading(true);
  153. const pagination = getPaginationRef() as PaginationProps;
  154. return list(deviceCode, deviceInfo, formData, pagination)
  155. .then(({ records, total, current }) => {
  156. setPagination({
  157. current,
  158. total,
  159. });
  160. records.forEach((item) => {
  161. Object.assign(item, item.readData);
  162. });
  163. data.value = records;
  164. })
  165. .finally(() => {
  166. setLoading(false);
  167. });
  168. }
  169. // 设备信息相关的字段
  170. const deviceInfo = ref<Record<string, unknown>>({});
  171. const deviceOptions = ref<Record<string, unknown>[]>([]);
  172. const dictOptions = ref<Record<string, unknown>[]>([]); // 子设备下拉框选项
  173. /**
  174. * 获取设备信息列表,初始化设备信息及设备可选项
  175. */
  176. async function fetchDevice() {
  177. const results = await getDeviceList({ devicetype: props.deviceCode, pageSize: 10000 });
  178. const dicts = await initDictOptions(props.dictCode);
  179. const options = results.map((item) => {
  180. return {
  181. label: item.strinstallpos,
  182. value: item.id || item.deviceID,
  183. deviceType: item.strtype || item.deviceType,
  184. devicekind: item.devicekind,
  185. stationtype: item.stationtype,
  186. };
  187. });
  188. deviceOptions.value = options;
  189. deviceInfo.value = results[0];
  190. dictOptions.value = dicts;
  191. }
  192. /**
  193. * 搜索,核心方法
  194. *
  195. * 该方法获取表单、处理表单数据后尝试获取数据并设置表头
  196. */
  197. async function search() {
  198. const form = getForm();
  199. await form.validate();
  200. const formData = form.getFieldsValue();
  201. deviceInfo.value = deviceOptions.value.find((opt) => {
  202. return opt.value === formData.gdeviceid;
  203. }) as Record<string, unknown>;
  204. const code = (deviceInfo.value.deviceType || props.deviceCode.concat('*')) as string;
  205. await fetchData(formData, code, deviceInfo.value);
  206. updateColumns(formData.deviceNum);
  207. }
  208. onMounted(async () => {
  209. await fetchDevice();
  210. // 生成所有需要查询的表头编码,例如 forcFan_auto 对应 forcFan_auto_history、forcFan_history 这两个
  211. const codes: string[] = [];
  212. if (deviceInfo.value.deviceType) {
  213. const arr = (deviceInfo.value.deviceType as string).split('_');
  214. while (arr.length) {
  215. codes.push(arr.join('_').concat('_history'));
  216. arr.pop();
  217. }
  218. }
  219. // 如此,例如deviceType为forcFan_auto, 则查询表头按 forcFan_auto_history、forcFan_history、columnsCode 为顺序
  220. initTable(codes.concat(props.columnsCode), deviceOptions.value, dictOptions.value);
  221. search();
  222. });
  223. </script>
  224. <style scoped lang="less">
  225. @import '/@/design/theme.less';
  226. :deep(.@{ventSpace}-table-body) {
  227. height: auto !important;
  228. }
  229. :deep(.zxm-picker) {
  230. height: 30px !important;
  231. }
  232. :deep(.zxm-table) {
  233. .zxm-table-title {
  234. display: none !important;
  235. }
  236. }
  237. .history-table {
  238. width: 100%;
  239. :deep(.jeecg-basic-table-form-container) {
  240. .@{ventSpace}-form {
  241. padding: 0 !important;
  242. border: none !important;
  243. margin-bottom: 0 !important;
  244. .@{ventSpace}-picker,
  245. .@{ventSpace}-select-selector {
  246. width: 100% !important;
  247. background: #00000017;
  248. border: 1px solid #b7b7b7;
  249. input,
  250. .@{ventSpace}-select-selection-item,
  251. .@{ventSpace}-picker-suffix {
  252. color: #fff;
  253. }
  254. .@{ventSpace}-select-selection-placeholder {
  255. color: #ffffffaa;
  256. }
  257. }
  258. }
  259. .@{ventSpace}-table-title {
  260. min-height: 0 !important;
  261. }
  262. }
  263. .pagination-box {
  264. display: flex;
  265. justify-content: flex-end;
  266. align-items: center;
  267. .page-num {
  268. border: 1px solid #0090d8;
  269. padding: 4px 8px;
  270. margin-right: 5px;
  271. color: #0090d8;
  272. }
  273. .btn {
  274. margin-right: 10px;
  275. }
  276. }
  277. }
  278. </style>