index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. <script setup lang="ts">
  2. import { useTableComponents } from '@/components/ZmTable/useTableComponents'
  3. import { IotProjectTaskScheduleApi } from '@/api/pms/iotprojecttaskschedule'
  4. import { IotProjectTaskApi, IotProjectTaskVO } from '@/api/pms/iotprojecttask'
  5. import * as DeptApi from '@/api/system/dept'
  6. import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
  7. import { dateFormatter } from '@/utils/formatTime'
  8. import download from '@/utils/download'
  9. import dayjs from 'dayjs'
  10. defineOptions({ name: 'IotProjectTask' })
  11. type ProjectTaskRow = IotProjectTaskVO & {
  12. manufactureName?: string
  13. contractName?: string
  14. contractCode?: string
  15. deptNames?: string
  16. statusLabel?: string
  17. createTime?: string
  18. }
  19. interface QueryParams extends PageParam {
  20. companyId?: number
  21. projectId?: number
  22. manufactureName?: string
  23. contractName?: string
  24. contractCode?: string
  25. wellName?: string
  26. wellType?: string
  27. location?: string
  28. technique?: string
  29. workloadDesign?: string
  30. createTime?: string[]
  31. userName?: string
  32. userId?: number
  33. platformFlag?: string
  34. remark?: string
  35. deptName?: string
  36. }
  37. interface PlanRow {
  38. id?: number
  39. status: string
  40. startTime: string
  41. endTime: string
  42. showEndTime: boolean
  43. [key: string]: any
  44. }
  45. interface PlanExtColumn {
  46. name: string
  47. identifier: string
  48. dataType?: string
  49. required?: number
  50. unit?: string
  51. defaultValue?: string | number | null
  52. sort?: number
  53. }
  54. const message = useMessage()
  55. const { t } = useI18n()
  56. const { push } = useRouter()
  57. const { ZmTable, ZmTableColumn } = useTableComponents<ProjectTaskRow>()
  58. const COMPLETED_STATUS = 'wg'
  59. const REPORT_DEPT_ID = 163
  60. const initQuery: QueryParams = {
  61. pageNo: 1,
  62. pageSize: 10,
  63. companyId: undefined,
  64. projectId: undefined,
  65. manufactureName: '',
  66. contractName: undefined,
  67. contractCode: undefined,
  68. wellName: undefined,
  69. wellType: undefined,
  70. location: undefined,
  71. technique: undefined,
  72. workloadDesign: undefined,
  73. createTime: [],
  74. userName: undefined,
  75. userId: undefined,
  76. platformFlag: '',
  77. remark: undefined,
  78. deptName: undefined
  79. }
  80. const queryParams = reactive<QueryParams>({ ...initQuery })
  81. const queryFormRef = ref()
  82. const loading = ref(false)
  83. const exportLoading = ref(false)
  84. const list = ref<ProjectTaskRow[]>([])
  85. const total = ref(0)
  86. const companyDeptList = ref<any[]>([])
  87. const planDialogVisible = ref(false)
  88. const planList = ref<PlanRow[]>([])
  89. const saveLoading = ref(false)
  90. const currentRow = ref<ProjectTaskRow | null>(null)
  91. const workProgressDictOptions = ref<any[]>([])
  92. const generateReportDialogVisible = ref(false)
  93. const generateReportLoading = ref(false)
  94. const generateReportRow = ref<ProjectTaskRow | null>(null)
  95. const reportDate = ref('')
  96. const getWorkProgressDictOptions = () => {
  97. workProgressDictOptions.value = getStrDictOptions(DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE)
  98. }
  99. const timestampToDateTime = (timestamp: number | string | null | undefined): string => {
  100. if (timestamp === null || timestamp === undefined || timestamp === '') return ''
  101. let value = typeof timestamp === 'string' ? Number.parseInt(timestamp, 10) : timestamp
  102. if (Number.isNaN(value)) return ''
  103. if (value < 1000000000000) value *= 1000
  104. return dayjs(value).format('YYYY-MM-DD HH:mm')
  105. }
  106. const currentTableColumns = ref<PlanExtColumn[]>([])
  107. const normalizeWorkloadColumns = (res: any): PlanExtColumn[] => {
  108. const columns = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
  109. return columns
  110. .filter((item: PlanExtColumn) => item?.identifier)
  111. .sort((a: PlanExtColumn, b: PlanExtColumn) => (a?.sort ?? 0) - (b?.sort ?? 0))
  112. }
  113. const isRequiredColumn = (column: PlanExtColumn) => Number(column.required) === 1
  114. const isEmptyValue = (value: unknown) =>
  115. value === undefined || value === null || (typeof value === 'string' && value.trim() === '')
  116. const getPlanDynamicValues = (row: PlanRow) =>
  117. currentTableColumns.value.reduce<Record<string, any>>((values, column) => {
  118. values[column.identifier] = row[column.identifier] ?? ''
  119. return values
  120. }, {})
  121. const createPlanRow = (plan?: any): PlanRow => {
  122. const row: PlanRow = {
  123. id: plan?.id,
  124. status: plan?.status || '',
  125. startTime: timestampToDateTime(plan?.startTime),
  126. endTime: timestampToDateTime(plan?.endTime),
  127. showEndTime: plan?.status !== COMPLETED_STATUS
  128. }
  129. currentTableColumns.value.forEach((column) => {
  130. row[column.identifier] = plan?.[column.identifier] ?? column.defaultValue ?? ''
  131. })
  132. return row
  133. }
  134. const openPlanDialog = async (row: ProjectTaskRow) => {
  135. currentRow.value = row
  136. planList.value = []
  137. currentTableColumns.value = []
  138. try {
  139. const res = await IotProjectTaskScheduleApi.getWorkload(row.id)
  140. currentTableColumns.value = normalizeWorkloadColumns(res)
  141. } catch {
  142. message.error('获取部门数据失败')
  143. }
  144. try {
  145. getWorkProgressDictOptions()
  146. const taskSchedules = await IotProjectTaskScheduleApi.getIotProjectTaskSchedules({
  147. taskId: row.id
  148. })
  149. if (taskSchedules?.length) {
  150. planList.value = taskSchedules.map((plan: any) => createPlanRow(plan))
  151. }
  152. planDialogVisible.value = true
  153. } catch {
  154. message.error('获取计划数据失败')
  155. }
  156. }
  157. const rowShowEndTime = (row: PlanRow) => row.showEndTime
  158. const onStatusChange = (row: PlanRow) => {
  159. if (row.status === COMPLETED_STATUS) {
  160. row.showEndTime = false
  161. row.endTime = ''
  162. return
  163. }
  164. row.showEndTime = true
  165. }
  166. const addNewRow = () => {
  167. planList.value.push(createPlanRow())
  168. }
  169. const removeRow = (index: number) => {
  170. planList.value.splice(index, 1)
  171. }
  172. const savePlan = async () => {
  173. try {
  174. saveLoading.value = true
  175. for (let index = 0; index < planList.value.length; index++) {
  176. const row = planList.value[index]
  177. if (!row.startTime) {
  178. message.error(`第${index + 1}行请选择开始时间`)
  179. return
  180. }
  181. const emptyRequiredColumn = currentTableColumns.value.find(
  182. (column) => isRequiredColumn(column) && isEmptyValue(row[column.identifier])
  183. )
  184. if (emptyRequiredColumn) {
  185. message.error(`第${index + 1}行请填写${emptyRequiredColumn.name}`)
  186. return
  187. }
  188. }
  189. const submitData = planList.value.map((item) => ({
  190. id: item.id,
  191. taskId: currentRow.value?.id,
  192. status: item.status,
  193. startTime: item.startTime ? new Date(item.startTime).getTime() : null,
  194. endTime:
  195. item.status !== COMPLETED_STATUS && item.endTime ? new Date(item.endTime).getTime() : null,
  196. ...getPlanDynamicValues(item)
  197. }))
  198. await IotProjectTaskScheduleApi.saveTaskSchedule(submitData)
  199. message.success('保存成功')
  200. planDialogVisible.value = false
  201. getList()
  202. } catch (error) {
  203. console.log(error)
  204. message.error('保存失败')
  205. } finally {
  206. saveLoading.value = false
  207. }
  208. }
  209. const openGenerateReportDialog = (row: ProjectTaskRow) => {
  210. generateReportRow.value = row
  211. reportDate.value = ''
  212. generateReportDialogVisible.value = true
  213. }
  214. const submitGenerateReport = async () => {
  215. if (!generateReportRow.value?.id) {
  216. message.error('未找到任务信息')
  217. return
  218. }
  219. if (!reportDate.value) {
  220. message.error('请选择日报日期')
  221. return
  222. }
  223. try {
  224. generateReportLoading.value = true
  225. await IotProjectTaskApi.generateReport({
  226. id: generateReportRow.value.id,
  227. reportDate: dayjs(reportDate.value).valueOf()
  228. })
  229. message.success('生成日报成功')
  230. generateReportDialogVisible.value = false
  231. } catch {
  232. message.error('生成日报失败')
  233. } finally {
  234. generateReportLoading.value = false
  235. }
  236. }
  237. const getList = async () => {
  238. loading.value = true
  239. try {
  240. const data = await IotProjectTaskApi.getIotProjectTaskList(queryParams)
  241. list.value = data.list
  242. total.value = data.total
  243. } finally {
  244. loading.value = false
  245. }
  246. }
  247. const handleQuery = () => {
  248. queryParams.pageNo = 1
  249. getList()
  250. }
  251. const resetQuery = () => {
  252. Object.assign(queryParams, { ...initQuery })
  253. queryFormRef.value?.resetFields()
  254. handleQuery()
  255. }
  256. const handleSizeChange = (val: number) => {
  257. queryParams.pageSize = val
  258. handleQuery()
  259. }
  260. const handleCurrentChange = (val: number) => {
  261. queryParams.pageNo = val
  262. getList()
  263. }
  264. const openForm = (type: string, id?: number, projectId?: number) => {
  265. if (id === undefined) {
  266. push({ name: 'IotProjectTaskInfo', params: { type } })
  267. return
  268. }
  269. push({ name: 'IotProjectTaskInfo', params: { type, id, projectId } })
  270. }
  271. const handleDelete = async (id: number) => {
  272. try {
  273. await message.delConfirm()
  274. await IotProjectTaskApi.deleteIotProjectTask(id)
  275. message.success(t('common.delSuccess'))
  276. await getList()
  277. } catch {}
  278. }
  279. const handleExport = async () => {
  280. try {
  281. exportLoading.value = true
  282. const data = await IotProjectTaskApi.exportIotProjectTask(queryParams)
  283. download.excel(data, '项目信息任务拆分.xls')
  284. } finally {
  285. exportLoading.value = false
  286. }
  287. }
  288. onMounted(async () => {
  289. companyDeptList.value = await DeptApi.companyLevelDepts()
  290. getWorkProgressDictOptions()
  291. getList()
  292. })
  293. </script>
  294. <template>
  295. <div
  296. class="iot-project-task-page grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
  297. <el-form
  298. ref="queryFormRef"
  299. :model="queryParams"
  300. size="default"
  301. label-width="72px"
  302. class="iot-project-task-query bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 py-3 min-w-0">
  303. <div class="query-row">
  304. <el-form-item label="公司" prop="companyId">
  305. <el-select
  306. v-model="queryParams.companyId"
  307. placeholder="请选择公司"
  308. clearable
  309. filterable
  310. class="w-full">
  311. <el-option
  312. v-for="item in companyDeptList"
  313. :key="item.id"
  314. :label="item.name"
  315. :value="item.id" />
  316. </el-select>
  317. </el-form-item>
  318. <el-form-item label="客户名称" prop="manufactureName">
  319. <el-input
  320. v-model="queryParams.manufactureName"
  321. placeholder="请输入客户名称"
  322. clearable
  323. class="w-full"
  324. @keyup.enter="handleQuery" />
  325. </el-form-item>
  326. <el-form-item label="合同名称" prop="contractName">
  327. <el-input
  328. v-model="queryParams.contractName"
  329. placeholder="请输入合同名称"
  330. clearable
  331. class="w-full"
  332. @keyup.enter="handleQuery" />
  333. </el-form-item>
  334. <el-form-item label="合同编号" prop="contractCode">
  335. <el-input
  336. v-model="queryParams.contractCode"
  337. placeholder="请输入合同编号"
  338. clearable
  339. class="w-full"
  340. @keyup.enter="handleQuery" />
  341. </el-form-item>
  342. <el-form-item label="施工队伍" prop="deptName">
  343. <el-input
  344. v-model="queryParams.deptName"
  345. placeholder="请输入施工队伍"
  346. clearable
  347. class="w-full"
  348. @keyup.enter="handleQuery" />
  349. </el-form-item>
  350. <el-form-item label="井号" prop="wellName">
  351. <el-input
  352. v-model="queryParams.wellName"
  353. placeholder="请输入井号"
  354. clearable
  355. class="w-full"
  356. @keyup.enter="handleQuery" />
  357. </el-form-item>
  358. <el-form-item label="平台井" prop="platformFlag">
  359. <el-select
  360. v-model="queryParams.platformFlag"
  361. placeholder="请选择平台井"
  362. clearable
  363. class="w-full"
  364. @change="handleQuery">
  365. <el-option label="全部" value="A" />
  366. <el-option label="是" value="Y" />
  367. <el-option label="否" value="N" />
  368. </el-select>
  369. </el-form-item>
  370. <el-form-item label="创建时间" prop="createTime">
  371. <el-date-picker
  372. v-model="queryParams.createTime"
  373. value-format="YYYY-MM-DD HH:mm:ss"
  374. type="daterange"
  375. start-placeholder="开始日期"
  376. end-placeholder="结束日期"
  377. :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  378. class="w-full" />
  379. </el-form-item>
  380. <el-form-item class="query-actions">
  381. <el-button type="primary" @click="handleQuery">
  382. <Icon icon="ep:search" class="mr-5px" />搜索
  383. </el-button>
  384. <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" />重置 </el-button>
  385. <el-button type="success" plain :loading="exportLoading" @click="handleExport">
  386. <Icon icon="ep:download" class="mr-5px" />导出
  387. </el-button>
  388. </el-form-item>
  389. </div>
  390. </el-form>
  391. <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-w-0 min-h-0">
  392. <div class="flex-1 relative min-h-0">
  393. <el-auto-resizer class="absolute">
  394. <template #default="{ width, height }">
  395. <ZmTable
  396. :data="list"
  397. :loading="loading"
  398. :width="width"
  399. :height="height"
  400. :max-height="height"
  401. show-border>
  402. <ZmTableColumn
  403. type="index"
  404. :label="t('monitor.serial')"
  405. :width="70"
  406. fixed="left"
  407. hide-in-column-settings />
  408. <ZmTableColumn prop="manufactureName" label="客户名称" min-width="180" fixed="left" />
  409. <ZmTableColumn prop="contractName" label="合同名称" min-width="220" />
  410. <ZmTableColumn prop="contractCode" label="合同编号" min-width="150" />
  411. <ZmTableColumn prop="wellName" label="井号" min-width="120" />
  412. <ZmTableColumn prop="location" label="施工地点" min-width="140" />
  413. <ZmTableColumn prop="deptNames" label="施工队伍" min-width="150" />
  414. <ZmTableColumn prop="statusLabel" label="施工状态" min-width="120" />
  415. <ZmTableColumn prop="createTime" label="创建时间" min-width="180">
  416. <template #default="{ row }">
  417. {{ dateFormatter(row, null, row.createTime) }}
  418. </template>
  419. </ZmTableColumn>
  420. <ZmTableColumn label="操作" width="220" fixed="right" action>
  421. <template #default="{ row }">
  422. <el-button
  423. link
  424. type="primary"
  425. @click="openPlanDialog(row)"
  426. v-hasPermi="['rq:iot-project-task:update']">
  427. 计划
  428. </el-button>
  429. <el-button
  430. v-if="Number(row.deptId) === REPORT_DEPT_ID"
  431. link
  432. type="primary"
  433. @click="openGenerateReportDialog(row)">
  434. 生成日报
  435. </el-button>
  436. <el-button
  437. link
  438. type="primary"
  439. @click="openForm('update', row.id, row.projectId)"
  440. v-hasPermi="['rq:iot-project-task:update']">
  441. 编辑
  442. </el-button>
  443. <el-button
  444. link
  445. type="danger"
  446. @click="handleDelete(row.id)"
  447. v-hasPermi="['rq:iot-project-task:delete']">
  448. 删除
  449. </el-button>
  450. </template>
  451. </ZmTableColumn>
  452. </ZmTable>
  453. </template>
  454. </el-auto-resizer>
  455. </div>
  456. <div class="h-8 mt-2 flex items-center justify-end">
  457. <el-pagination
  458. v-show="total > 0"
  459. size="default"
  460. :current-page="queryParams.pageNo"
  461. :page-size="queryParams.pageSize"
  462. :background="true"
  463. :page-sizes="[10, 20, 30, 50, 100]"
  464. :total="total"
  465. layout="total, sizes, prev, pager, next, jumper"
  466. @size-change="handleSizeChange"
  467. @current-change="handleCurrentChange" />
  468. </div>
  469. </div>
  470. </div>
  471. <el-dialog
  472. v-model="planDialogVisible"
  473. :title="`${currentRow?.contractName || ''} - ${currentRow?.wellName || ''} - 任务计划`"
  474. width="80%">
  475. <div class="mb-15px">
  476. <el-button type="primary" @click="addNewRow">
  477. <Icon icon="ep:plus" class="mr-5px" />新增行
  478. </el-button>
  479. </div>
  480. <el-table :data="planList" border stripe>
  481. <el-table-column label="序号" width="60" align="center">
  482. <template #default="scope">
  483. {{ scope.$index + 1 }}
  484. </template>
  485. </el-table-column>
  486. <!-- <el-table-column label="施工状态" min-width="200">
  487. <template #default="scope">
  488. <el-select
  489. v-model="scope.row.status"
  490. placeholder="请选择施工状态"
  491. clearable
  492. class="w-full"
  493. @change="onStatusChange(scope.row)">
  494. <el-option
  495. v-for="dict in workProgressDictOptions"
  496. :key="dict.value"
  497. :label="dict.label"
  498. :value="dict.value" />
  499. </el-select>
  500. </template>
  501. </el-table-column> -->
  502. <el-table-column label="开始时间" min-width="200">
  503. <template #default="scope">
  504. <el-date-picker
  505. v-model="scope.row.startTime"
  506. type="datetime"
  507. placeholder="选择开始时间"
  508. value-format="YYYY-MM-DD HH:mm"
  509. format="YYYY-MM-DD HH:mm"
  510. class="w-full" />
  511. </template>
  512. </el-table-column>
  513. <el-table-column label="结束时间" min-width="200">
  514. <template #default="scope">
  515. <el-date-picker
  516. v-if="rowShowEndTime(scope.row)"
  517. v-model="scope.row.endTime"
  518. type="datetime"
  519. placeholder="选择结束时间"
  520. value-format="YYYY-MM-DD HH:mm"
  521. format="YYYY-MM-DD HH:mm"
  522. class="w-full" />
  523. </template>
  524. </el-table-column>
  525. <el-table-column
  526. min-width="100"
  527. v-for="item in currentTableColumns"
  528. :key="item.identifier"
  529. :prop="item.identifier">
  530. <template #header>
  531. <span>
  532. <span v-if="isRequiredColumn(item)" class="required-mark">*</span>
  533. {{ item.unit ? `${item.name}(${item.unit})` : item.name }}
  534. </span>
  535. </template>
  536. <template #default="scope">
  537. <!-- <el-input type="number" v-model="scope.row[item.identifier]" placeholder="请输入" /> -->
  538. <el-input-number
  539. style="width: 150px"
  540. v-model="scope.row[item.identifier]"
  541. :min="0"
  542. placeholder="请输入" />
  543. </template>
  544. </el-table-column>
  545. <el-table-column label="操作" width="100" align="center">
  546. <template #default="scope">
  547. <el-button link type="danger" @click="removeRow(scope.$index)">删除</el-button>
  548. </template>
  549. </el-table-column>
  550. </el-table>
  551. <template #footer>
  552. <span class="dialog-footer">
  553. <el-button @click="planDialogVisible = false">取消</el-button>
  554. <el-button type="primary" :loading="saveLoading" @click="savePlan">保存</el-button>
  555. </span>
  556. </template>
  557. </el-dialog>
  558. <el-dialog v-model="generateReportDialogVisible" title="生成日报" width="420px">
  559. <el-form size="default" label-width="80px">
  560. <el-form-item label="日报日期" required>
  561. <el-date-picker
  562. v-model="reportDate"
  563. type="date"
  564. placeholder="请选择日期"
  565. value-format="YYYY-MM-DD"
  566. format="YYYY-MM-DD"
  567. class="w-full!" />
  568. </el-form-item>
  569. </el-form>
  570. <template #footer>
  571. <span class="dialog-footer">
  572. <el-button size="default" @click="generateReportDialogVisible = false">取消</el-button>
  573. <el-button
  574. size="default"
  575. type="primary"
  576. :loading="generateReportLoading"
  577. @click="submitGenerateReport">
  578. 确定
  579. </el-button>
  580. </span>
  581. </template>
  582. </el-dialog>
  583. <!-- 生成日报 Dialog -->
  584. <el-dialog v-model="generateReportDialogVisible" title="生成日报" width="420px">
  585. <el-form size="default" label-width="80px">
  586. <el-form-item label="日报日期" required>
  587. <el-date-picker
  588. v-model="reportDate"
  589. type="date"
  590. placeholder="请选择日期"
  591. value-format="YYYY-MM-DD"
  592. format="YYYY-MM-DD"
  593. class="w-full!" />
  594. </el-form-item>
  595. </el-form>
  596. <template #footer>
  597. <span class="dialog-footer">
  598. <el-button size="default" @click="generateReportDialogVisible = false">取消</el-button>
  599. <el-button
  600. size="default"
  601. type="primary"
  602. @click="submitGenerateReport"
  603. :loading="generateReportLoading">
  604. 确定
  605. </el-button>
  606. </span>
  607. </template>
  608. </el-dialog>
  609. </template>
  610. <style scoped>
  611. .iot-project-task-query {
  612. display: block;
  613. }
  614. .query-row {
  615. display: grid;
  616. grid-template-columns: repeat(4, minmax(270px, 1fr));
  617. align-items: center;
  618. gap: 14px 28px;
  619. min-width: 0;
  620. }
  621. .query-actions {
  622. justify-self: end;
  623. }
  624. .query-actions :deep(.el-form-item__content) {
  625. display: flex;
  626. flex-wrap: wrap;
  627. justify-content: flex-end;
  628. gap: 8px 10px;
  629. }
  630. .query-actions :deep(.el-button) {
  631. margin-left: 0;
  632. }
  633. :deep(.el-form-item) {
  634. margin-bottom: 0;
  635. }
  636. .required-mark {
  637. margin-right: 2px;
  638. color: var(--el-color-danger);
  639. }
  640. @media (width >= 2200px) {
  641. .query-actions {
  642. grid-column: 4;
  643. }
  644. }
  645. @media (width <= 1800px) {
  646. .query-row {
  647. grid-template-columns: repeat(3, minmax(270px, 1fr));
  648. }
  649. .query-actions {
  650. justify-self: start;
  651. }
  652. }
  653. @media (width <= 1500px) {
  654. .query-row {
  655. grid-template-columns: repeat(2, minmax(260px, 1fr));
  656. gap: 12px 18px;
  657. }
  658. .query-control {
  659. width: 210px;
  660. }
  661. .query-control--small {
  662. width: 160px;
  663. }
  664. .query-control--date {
  665. width: 280px;
  666. }
  667. }
  668. @media (width <= 1200px) {
  669. .iot-project-task-page {
  670. grid-template-rows: auto minmax(480px, 1fr);
  671. height: auto;
  672. min-height: calc(
  673. 100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
  674. );
  675. }
  676. .query-actions {
  677. width: 100%;
  678. justify-self: start;
  679. }
  680. .query-actions :deep(.el-form-item__content) {
  681. justify-content: flex-start;
  682. }
  683. }
  684. @media (width <= 768px) {
  685. .iot-project-task-query {
  686. padding: 12px;
  687. }
  688. .query-row,
  689. .query-row :deep(.el-form-item),
  690. .query-actions {
  691. width: 100%;
  692. }
  693. .query-row {
  694. grid-template-columns: minmax(0, 1fr);
  695. }
  696. .query-control,
  697. .query-control--small,
  698. .query-control--date {
  699. width: 100%;
  700. }
  701. .query-actions :deep(.el-form-item__content) {
  702. display: grid;
  703. grid-template-columns: repeat(2, minmax(0, 1fr));
  704. gap: 8px;
  705. width: 100%;
  706. }
  707. .query-actions :deep(.el-button) {
  708. width: 100%;
  709. margin-left: 0;
  710. }
  711. }
  712. </style>