Przeglądaj źródła

feat(pms): 完善计划工作填报与维护功能

- 新增计划工作分页列表及单位展示
- 支持计划工作新增、编辑和只读查看
- 接入分页、详情、新增、更新及扩展属性接口
- 支持部门树筛选及公司层级自动匹配
- 根据扩展属性动态过滤可选项目业务
- 根据业务映射计划工作量字段及单位
- 增加计划工作业务数据字典
- 为汇总表单位列增加表格配置入口
Zimo 1 tydzień temu
rodzic
commit
b18c7a33e7

+ 71 - 0
src/api/pms/iotprojecttaskplan/index.ts

@@ -7,6 +7,48 @@ export interface IotProjectTaskPlanSummaryParams {
   startTime?: string[]
 }
 
+export type IotProjectTaskPlanPageParams = IotProjectTaskPlanSummaryParams
+
+export interface IotProjectTaskPlanPageVO {
+  id: number
+  deptName: string
+  business: string
+  yearMonth: string
+  workloadUnit: string
+  planGasInjection?: number | string | null
+  planLayers?: number | string | null
+  planWellTrips?: number | string | null
+  planFootage?: number | string | null
+}
+
+export interface IotProjectTaskPlanPageResult {
+  list: IotProjectTaskPlanPageVO[]
+  total: number
+}
+
+export interface IotProjectTaskPlanExtProperty {
+  identifier: string
+  [key: string]: unknown
+}
+
+export interface IotProjectTaskPlanCreateData {
+  deptId: number
+  companyId: number
+  startTime: number
+  business: string
+  workloadUnit: string
+  planGasInjection?: number
+  planLayers?: number
+  planWellTrips?: number
+  planFootage?: number
+}
+
+export interface IotProjectTaskPlanUpdateData extends IotProjectTaskPlanCreateData {
+  id: number
+}
+
+export interface IotProjectTaskPlanDetailVO extends IotProjectTaskPlanUpdateData {}
+
 export interface IotProjectTaskPlanSummaryItem {
   yearMonth: string
   cumulativeGasInjection?: number | string | null
@@ -33,6 +75,35 @@ export interface IotProjectTaskPlanSummaryResult {
 }
 
 export const IotProjectTaskPlanApi = {
+  create: async (data: IotProjectTaskPlanCreateData) => {
+    return await request.post<number>({
+      url: '/pms/iot-project-task-plan/create',
+      data
+    })
+  },
+  update: async (data: IotProjectTaskPlanUpdateData) => {
+    return await request.put<boolean>({
+      url: '/pms/iot-project-task-plan/update',
+      data
+    })
+  },
+  get: async (id: number) => {
+    return await request.get<IotProjectTaskPlanDetailVO>({
+      url: `/pms/iot-project-task-plan/get?id=${id}`
+    })
+  },
+  getPlanExtProperties: async (deptId: number) => {
+    return await request.get<IotProjectTaskPlanExtProperty[]>({
+      url: '/pms/iot-project-task-plan/planExtProperties',
+      params: { deptId }
+    })
+  },
+  getPage: async (params: IotProjectTaskPlanPageParams) => {
+    return await request.get<IotProjectTaskPlanPageResult>({
+      url: '/pms/iot-project-task-plan/page',
+      params
+    })
+  },
   getSummaryStatistics: async (params: IotProjectTaskPlanSummaryParams) => {
     return await request.get<IotProjectTaskPlanSummaryResult>({
       url: '/pms/iot-project-task-plan/summaryStatistics',

+ 1 - 0
src/utils/dict.ts

@@ -282,6 +282,7 @@ export enum DICT_TYPE {
   RQ_IOT_MODEL_TEMPLATE_ATTR = 'rq_iot_model_template_attr',
   RQ_IOT_MODEL_PLUS = 'rq_iot_model_plus',
   RQ_IOT_SUM = 'rq_iot_isSum', //是否累计
+  RQ_IOT_WORKLOAD_PLAN_BIZ = 'rq_iot_workload_plan_biz', // 计划工作业务
   PMS_ORDER_PROCESS_MODE = 'pms_main_work_order_process_mode', // 保养方式 0内部保养  1委外保养
   PMS_THING_MODEL_UNIT = 'pms_thing_model_unit', // pms属性模板单位
   PMS_PROJECT_TASK_SCHEDULE = 'constructionStatus', // 日报 项目管理 任务计划

+ 659 - 0
src/views/pms/plan-work/fill.vue

@@ -0,0 +1,659 @@
+<script lang="ts" setup>
+import {
+  IotProjectTaskPlanApi,
+  type IotProjectTaskPlanCreateData,
+  type IotProjectTaskPlanExtProperty,
+  type IotProjectTaskPlanPageParams,
+  type IotProjectTaskPlanPageVO,
+  type IotProjectTaskPlanUpdateData
+} from '@/api/pms/iotprojecttaskplan'
+import * as DeptApi from '@/api/system/dept'
+import { useUserStore } from '@/store/modules/user'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { rangeShortcuts } from '@/utils/formatTime'
+import { defaultProps, handleTree } from '@/utils/tree'
+import { useDebounceFn } from '@vueuse/core'
+import dayjs from 'dayjs'
+import type { FormInstance, FormRules } from 'element-plus'
+
+defineOptions({ name: 'PlanWorkFill' })
+
+const TOP_DEPT_ID = 156
+const deptId = useUserStore().getUser.deptId
+const message = useMessage()
+
+const createDefaultQuery = (): IotProjectTaskPlanPageParams => ({
+  pageNo: 1,
+  pageSize: 10,
+  deptId,
+  startTime: [...rangeShortcuts[8].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))]
+})
+
+const query = ref<IotProjectTaskPlanPageParams>(createDefaultQuery())
+const loading = ref(false)
+const list = ref<IotProjectTaskPlanPageVO[]>([])
+const total = ref(0)
+
+type PlanField = 'planGasInjection' | 'planLayers' | 'planWellTrips' | 'planFootage'
+
+interface BusinessPlanConfig {
+  field: PlanField
+  unit: string
+}
+
+interface DeptOption {
+  id: number
+  name: string
+  parentId?: number
+  type?: string
+  disabled?: boolean
+  children?: DeptOption[]
+}
+
+interface CreatePlanForm {
+  id?: number
+  deptId?: number
+  companyId?: number
+  startTime?: number | string
+  business: string
+  workloadUnit: string
+  planWorkload?: number
+}
+
+type FormMode = 'create' | 'edit' | 'view'
+
+const BUSINESS_PLAN_CONFIG: Record<string, BusinessPlanConfig> = {
+  注气: { field: 'planGasInjection', unit: '万方' },
+  压裂: { field: 'planLayers', unit: '层' },
+  连油: { field: 'planWellTrips', unit: '井' },
+  钻井: { field: 'planFootage', unit: '米' },
+  修井: { field: 'planWellTrips', unit: '井' },
+  井下: { field: 'planWellTrips', unit: '井' }
+}
+
+const businessOptions = getStrDictOptions(DICT_TYPE.RQ_IOT_WORKLOAD_PLAN_BIZ)
+const deptTreeProps = { ...defaultProps, disabled: 'disabled' }
+
+const getBusinessPlanConfig = (business: string) => {
+  const businessValue = String(business || '').trim()
+  const businessLabel = businessOptions.find((option) => option.value === businessValue)?.label
+  return BUSINESS_PLAN_CONFIG[businessValue] || BUSINESS_PLAN_CONFIG[businessLabel || '']
+}
+
+const getPlanWorkload = (row: IotProjectTaskPlanPageVO) => {
+  const config = getBusinessPlanConfig(row.business)
+  if (!config) return '-'
+
+  const value = row[config.field]
+  return value === null || value === undefined || value === '' ? '-' : value
+}
+
+const createDialogVisible = ref(false)
+const createLoading = ref(false)
+const formMode = ref<FormMode>('create')
+const deptOptionsLoading = ref(false)
+const extPropertiesLoading = ref(false)
+const deptOptions = ref<DeptOption[]>([])
+const flatDeptOptions = ref<DeptOption[]>([])
+const planExtProperties = ref<IotProjectTaskPlanExtProperty[]>([])
+const createFormRef = ref<FormInstance>()
+
+const isReadonly = computed(() => formMode.value === 'view')
+const dialogTitle = computed(() => {
+  if (formMode.value === 'edit') return '编辑计划工作'
+  if (formMode.value === 'view') return '查看计划工作'
+  return '新增计划工作'
+})
+const dialogDescription = computed(() =>
+  isReadonly.value ? '查看部门项目业务计划工作量详情' : '维护部门各月份的项目业务计划工作量'
+)
+
+const selectableBusinessOptions = computed(() => {
+  const identifiers = new Set(planExtProperties.value.map((property) => property.identifier))
+
+  return businessOptions.filter((option) => {
+    const config = getBusinessPlanConfig(option.value)
+    return config ? identifiers.has(config.field) : false
+  })
+})
+
+const createInitialForm = (): CreatePlanForm => ({
+  id: undefined,
+  deptId: undefined,
+  companyId: undefined,
+  startTime: dayjs().startOf('month').valueOf(),
+  business: '',
+  workloadUnit: '',
+  planWorkload: undefined
+})
+
+const createFormData = ref<CreatePlanForm>(createInitialForm())
+const createFormRules: FormRules = {
+  deptId: [{ required: true, message: '请选择部门', trigger: 'change' }],
+  startTime: [{ required: true, message: '请选择时间', trigger: 'change' }],
+  business: [{ required: true, message: '请选择项目业务', trigger: 'change' }],
+  planWorkload: [{ required: true, message: '请输入计划工作量', trigger: 'blur' }]
+}
+
+const loadDeptOptions = async () => {
+  if (deptOptions.value.length) return
+
+  deptOptionsLoading.value = true
+  try {
+    const data = (await DeptApi.specifiedSimpleDepts(TOP_DEPT_ID)) as DeptOption[]
+    flatDeptOptions.value = data.map((dept) => ({
+      ...dept,
+      type: String(dept.type || ''),
+      disabled: String(dept.type || '') !== '3'
+    }))
+    deptOptions.value = handleTree(flatDeptOptions.value)
+  } finally {
+    deptOptionsLoading.value = false
+  }
+}
+
+const findCompanyId = (selectedDeptId: number) => {
+  const deptMap = new Map(flatDeptOptions.value.map((dept) => [dept.id, dept]))
+  const visitedIds = new Set<number>()
+  let currentDept = deptMap.get(selectedDeptId)
+
+  while (currentDept && !visitedIds.has(currentDept.id)) {
+    visitedIds.add(currentDept.id)
+    if (String(currentDept.type) === '1') return currentDept.id
+    currentDept = currentDept.parentId ? deptMap.get(currentDept.parentId) : undefined
+  }
+
+  return undefined
+}
+
+const loadPlanExtProperties = async (selectedDeptId?: number) => {
+  planExtProperties.value = []
+  if (!selectedDeptId) return
+
+  extPropertiesLoading.value = true
+  try {
+    const data = await IotProjectTaskPlanApi.getPlanExtProperties(selectedDeptId)
+    planExtProperties.value = Array.isArray(data) ? data : []
+  } finally {
+    extPropertiesLoading.value = false
+  }
+}
+
+const handleDeptChange = async (selectedDeptId?: number) => {
+  createFormData.value.companyId = selectedDeptId ? findCompanyId(selectedDeptId) : undefined
+  createFormData.value.business = ''
+  createFormData.value.workloadUnit = ''
+  createFormData.value.planWorkload = undefined
+  await loadPlanExtProperties(selectedDeptId)
+}
+
+const normalizeStartTime = (value: unknown) => {
+  const timestamp = Number(value)
+  if (Number.isFinite(timestamp) && timestamp > 0) return timestamp
+
+  const parsedTimestamp = dayjs(value as string).valueOf()
+  return Number.isFinite(parsedTimestamp) ? parsedTimestamp : undefined
+}
+
+const handleOpenForm = async (mode: FormMode, id?: number) => {
+  formMode.value = mode
+  createFormData.value = createInitialForm()
+  createDialogVisible.value = true
+  createLoading.value = true
+  try {
+    await loadDeptOptions()
+
+    if (id) {
+      const data = await IotProjectTaskPlanApi.get(id)
+      const business = String(data.business || '')
+      const config = getBusinessPlanConfig(business)
+      const workloadValue = config ? data[config.field] : undefined
+
+      createFormData.value = {
+        id: data.id,
+        deptId: data.deptId,
+        companyId: data.companyId || findCompanyId(data.deptId),
+        startTime: normalizeStartTime(data.startTime),
+        business,
+        workloadUnit: data.workloadUnit || config?.unit || '',
+        planWorkload:
+          workloadValue === null || workloadValue === undefined ? undefined : Number(workloadValue)
+      }
+      await loadPlanExtProperties(data.deptId)
+    }
+
+    await nextTick()
+    createFormRef.value?.clearValidate()
+  } finally {
+    createLoading.value = false
+  }
+}
+
+const handleOpenCreate = () => handleOpenForm('create')
+
+const handleBusinessChange = () => {
+  const config = getBusinessPlanConfig(createFormData.value.business)
+  createFormData.value.workloadUnit = config?.unit || ''
+  createFormData.value.planWorkload = undefined
+}
+
+const submitCreate = async () => {
+  if (isReadonly.value) return
+  await createFormRef.value?.validate()
+
+  const {
+    deptId: formDeptId,
+    companyId,
+    startTime,
+    business,
+    workloadUnit,
+    planWorkload
+  } = createFormData.value
+  const config = getBusinessPlanConfig(business)
+
+  if (
+    !formDeptId ||
+    !companyId ||
+    !startTime ||
+    !workloadUnit ||
+    planWorkload === undefined ||
+    !config
+  ) {
+    message.error('表单数据不完整或项目业务未配置计划工作量字段')
+    return
+  }
+
+  const data: IotProjectTaskPlanCreateData = {
+    deptId: formDeptId,
+    companyId,
+    startTime: dayjs(Number(startTime)).startOf('month').valueOf(),
+    business,
+    workloadUnit
+  }
+  data[config.field] = planWorkload
+
+  createLoading.value = true
+  try {
+    if (formMode.value === 'edit') {
+      if (!createFormData.value.id) return
+      await IotProjectTaskPlanApi.update({
+        ...data,
+        id: createFormData.value.id
+      } as IotProjectTaskPlanUpdateData)
+      message.success('编辑成功')
+    } else {
+      await IotProjectTaskPlanApi.create(data)
+      message.success('新增成功')
+    }
+    createDialogVisible.value = false
+    handleQuery()
+  } finally {
+    createLoading.value = false
+  }
+}
+
+const loadPage = useDebounceFn(async () => {
+  loading.value = true
+  try {
+    const data = await IotProjectTaskPlanApi.getPage(query.value)
+    console.log('计划工作分页接口数据:', data)
+    list.value = Array.isArray(data?.list) ? data.list : []
+    total.value = Number(data?.total || 0)
+  } finally {
+    loading.value = false
+  }
+}, 200)
+
+const handleQuery = () => {
+  query.value.pageNo = 1
+  loadPage()
+}
+
+const handleSizeChange = (pageSize: number) => {
+  query.value.pageSize = pageSize
+  handleQuery()
+}
+
+const handleCurrentChange = (pageNo: number) => {
+  query.value.pageNo = pageNo
+  loadPage()
+}
+
+const resetQuery = () => {
+  query.value = createDefaultQuery()
+  handleQuery()
+}
+
+watch(
+  [() => query.value.deptId, () => query.value.startTime],
+  () => {
+    handleQuery()
+  },
+  { immediate: true }
+)
+</script>
+
+<template>
+  <div
+    class="grid grid-cols-[auto_minmax(0,1fr)] grid-rows-[62px_minmax(0,1fr)] gap-4 w-full min-w-0 overflow-hidden h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
+    <DeptTreeSelect
+      :top-id="TOP_DEPT_ID"
+      :deptId="deptId"
+      v-model="query.deptId"
+      :show-title="false"
+      class="row-span-2" />
+
+    <el-form
+      size="default"
+      class="plan-query-form bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 flex items-center justify-between min-w-0 overflow-hidden">
+      <el-form-item label="时间范围">
+        <el-date-picker
+          v-model="query.startTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :shortcuts="rangeShortcuts"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-260px" />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" plain @click="handleOpenCreate">
+          <Icon icon="ep:plus" class="mr-5px" />新增
+        </el-button>
+        <el-button type="primary" :loading="loading" @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />搜索
+        </el-button>
+        <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" />重置 </el-button>
+      </el-form-item>
+    </el-form>
+
+    <div
+      class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow min-h-0 min-w-0 w-full max-w-full p-4 overflow-hidden flex flex-col">
+      <div class="relative flex-1 min-h-0 min-w-0 overflow-hidden">
+        <el-auto-resizer class="absolute inset-0">
+          <template #default="{ width, height }">
+            <ZmTable
+              :data="list"
+              :loading="loading"
+              :width="width"
+              :height="height"
+              :max-height="height"
+              empty-text="暂无数据"
+              show-border>
+              <ZmTableColumn prop="deptName" label="部门名称" fixed="left" show-overflow-tooltip />
+              <ZmTableColumn prop="business" label="项目业务" />
+              <ZmTableColumn prop="yearMonth" label="时间" />
+              <ZmTableColumn label="计划工作量" cover-formatter :real-value="getPlanWorkload" />
+              <ZmTableColumn prop="workloadUnit" label="工作量单位" />
+              <ZmTableColumn label="操作" :width="140" fixed="right" action>
+                <template #default="{ row }">
+                  <el-button
+                    size="default"
+                    type="success"
+                    link
+                    @click="handleOpenForm('view', row.id)">
+                    查看
+                  </el-button>
+                  <el-button
+                    size="default"
+                    type="primary"
+                    link
+                    @click="handleOpenForm('edit', row.id)">
+                    编辑
+                  </el-button>
+                </template>
+              </ZmTableColumn>
+            </ZmTable>
+          </template>
+        </el-auto-resizer>
+      </div>
+
+      <div class="h-8 mt-2 flex items-center justify-end">
+        <el-pagination
+          size="default"
+          v-show="total > 0"
+          :current-page="query.pageNo"
+          :page-size="query.pageSize"
+          :page-sizes="[10, 20, 30, 50, 100]"
+          :total="total"
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange" />
+      </div>
+    </div>
+  </div>
+
+  <Dialog v-model="createDialogVisible" :title="dialogTitle" width="780px" class="plan-work-dialog">
+    <template #title>
+      <div class="dialog-title">
+        <div class="dialog-title__icon">
+          <Icon icon="ep:calendar" :size="22" />
+        </div>
+        <div>
+          <div class="dialog-title__text">{{ dialogTitle }}</div>
+          <div class="dialog-title__desc">{{ dialogDescription }}</div>
+        </div>
+      </div>
+    </template>
+
+    <div v-loading="createLoading" class="plan-form-body">
+      <section class="plan-form-card">
+        <div class="plan-form-card__header">
+          <div class="plan-form-card__title">
+            <span class="plan-form-card__marker"></span>
+            <span>计划信息</span>
+          </div>
+        </div>
+
+        <el-form
+          ref="createFormRef"
+          :model="createFormData"
+          :rules="createFormRules"
+          :disabled="isReadonly"
+          size="default"
+          label-position="top"
+          class="plan-create-form">
+          <el-row :gutter="20">
+            <el-col :xs="24" :sm="12">
+              <el-form-item label="选择部门" prop="deptId">
+                <el-tree-select
+                  v-model="createFormData.deptId"
+                  :data="deptOptions"
+                  :props="deptTreeProps"
+                  :loading="deptOptionsLoading"
+                  :disabled="isReadonly"
+                  node-key="id"
+                  check-strictly
+                  default-expand-all
+                  filterable
+                  clearable
+                  placeholder="请选择部门"
+                  class="w-full"
+                  @change="handleDeptChange" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :xs="24" :sm="12">
+              <el-form-item label="选择时间" prop="startTime">
+                <el-date-picker
+                  v-model="createFormData.startTime"
+                  type="month"
+                  value-format="x"
+                  format="YYYY年MM月"
+                  :disabled="isReadonly"
+                  clearable
+                  placeholder="请选择年月"
+                  class="!w-full" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :xs="24" :sm="12">
+              <el-form-item label="项目业务" prop="business">
+                <el-select
+                  v-model="createFormData.business"
+                  :placeholder="createFormData.deptId ? '请选择项目业务' : '请先选择部门'"
+                  :loading="extPropertiesLoading"
+                  :disabled="isReadonly || !createFormData.deptId"
+                  clearable
+                  class="w-full"
+                  @change="handleBusinessChange">
+                  <el-option
+                    v-for="option in selectableBusinessOptions"
+                    :key="option.value"
+                    :label="option.label"
+                    :value="option.value" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+
+            <el-col :xs="24" :sm="12">
+              <el-form-item label="工作量单位">
+                <el-input
+                  v-model="createFormData.workloadUnit"
+                  readonly
+                  placeholder="选择项目业务后自动填入" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :xs="24">
+              <el-form-item label="计划工作量" prop="planWorkload">
+                <el-input-number
+                  align="left"
+                  v-model="createFormData.planWorkload"
+                  :min="0"
+                  :controls="false"
+                  :disabled="isReadonly || !createFormData.business"
+                  :placeholder="createFormData.business ? '请输入计划工作量' : '请先选择项目业务'"
+                  class="!w-full" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </section>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button size="default" :disabled="createLoading" @click="createDialogVisible = false">
+          {{ isReadonly ? '关 闭' : '取 消' }}
+        </el-button>
+        <el-button
+          v-if="!isReadonly"
+          size="default"
+          type="primary"
+          :loading="createLoading"
+          @click="submitCreate">
+          确 定
+        </el-button>
+      </div>
+    </template>
+  </Dialog>
+</template>
+
+<style scoped>
+.plan-query-form :deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+.dialog-title {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+}
+
+.dialog-title__icon {
+  display: flex;
+  width: 40px;
+  height: 40px;
+  color: var(--el-color-primary);
+  background: rgb(64 158 255 / 12%);
+  border: 1px solid rgb(64 158 255 / 18%);
+  border-radius: 8px;
+  align-items: center;
+  justify-content: center;
+}
+
+.dialog-title__text {
+  font-size: 17px;
+  font-weight: 600;
+  line-height: 22px;
+  color: var(--el-text-color-primary);
+}
+
+.dialog-title__desc {
+  margin-top: 3px;
+  font-size: 13px;
+  line-height: 18px;
+  color: var(--el-text-color-secondary);
+}
+
+.plan-form-body {
+  padding: 4px 0;
+}
+
+.plan-form-card {
+  overflow: hidden;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 8px 24px rgb(31 45 61 / 5%);
+}
+
+.plan-form-card__header {
+  display: flex;
+  min-height: 52px;
+  padding: 0 18px;
+  background: linear-gradient(90deg, rgb(64 158 255 / 8%) 0%, rgb(255 255 255 / 0%) 72%);
+  border-bottom: 1px solid var(--el-border-color-lighter);
+  align-items: center;
+}
+
+.plan-form-card__title {
+  display: flex;
+  gap: 10px;
+  font-size: 15px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+  align-items: center;
+}
+
+.plan-form-card__marker {
+  width: 4px;
+  height: 17px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.plan-create-form {
+  padding: 20px 22px 4px;
+}
+
+.plan-create-form :deep(.el-form-item) {
+  margin-bottom: 18px;
+}
+
+.plan-create-form :deep(.el-form-item__label) {
+  margin-bottom: 6px;
+  line-height: 18px;
+  color: var(--el-text-color-secondary);
+}
+
+.dialog-footer {
+  display: flex;
+  gap: 12px;
+  justify-content: flex-end;
+}
+
+@media (width <= 768px) {
+  .dialog-title__desc {
+    display: none;
+  }
+
+  .plan-create-form {
+    padding: 16px 16px 0;
+  }
+}
+</style>

+ 1 - 1
src/views/pms/plan-work/index.vue

@@ -266,7 +266,7 @@ watch(
                   {{ formatValue(row.total) }}
                 </template>
               </ZmTableColumn>
-              <ZmTableColumn prop="unit" label="单位" fixed="right" />
+              <ZmTableColumn prop="unit" label="单位" fixed="right" action />
             </ZmTable>
           </template>
         </el-auto-resizer>