Sfoglia il codice sorgente

移动端适配

Co-authored-by: Copilot <copilot@github.com>
Zimo 4 giorni fa
parent
commit
de8cd18635

+ 1 - 0
src/api/system/dept/index.ts

@@ -13,6 +13,7 @@ export interface DeptVO {
   costCenterIds: string[]
   stockLocationIds: string[]
   createTime: Date
+  type: string
 }
 
 // 查询部门(精简)列表

+ 189 - 16
src/views/pms/operation-meeting/index.vue

@@ -6,7 +6,6 @@ import { rangeShortcuts } from '@/utils/formatTime'
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
 import MeetingForm from './meeting-form.vue'
 import dayjs from 'dayjs'
-import { useUserStore } from '@/store/modules/user'
 
 interface Query {
   pageNo: number
@@ -15,9 +14,6 @@ interface Query {
   meetingDate: string[]
 }
 
-const userStore = useUserStore()
-const userDeptId = userStore.getUser.deptId
-
 const createInitialQuery = (): Query => ({
   pageNo: 1,
   pageSize: 10,
@@ -40,7 +36,9 @@ const deptOptions = ref<DeptOption[]>([])
 async function getDeptOptions() {
   const deptList = await getSimpleDeptList()
   deptOptions.value = deptList
-    .filter((item) => item.id === userDeptId)
+    .filter((item) => {
+      return item.type === '1'
+    })
     .map((item) => ({
       label: item.name,
       value: item.id as number
@@ -100,21 +98,25 @@ function handleView(id: number) {
   currentId.value = id
   visible.value = true
 }
+
+function formatMeetingDate(row: Pick<OperationMeetingListItem, 'meetingDate'>) {
+  return row.meetingDate ? dayjs(row.meetingDate).format('YYYY-MM-DD') : '-'
+}
 </script>
 <template>
   <div
-    class="min-w-0 overflow-x-hidden grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="operation-meeting-page min-w-0 overflow-x-hidden grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <el-form
       size="default"
-      label-position="top"
+      label-position="left"
       class="operation-meeting-query min-w-0 overflow-hidden rounded-lg bg-white p-4 shadow dark:bg-[#1d1e1f]"
     >
       <div class="min-w-0 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
         <div
           class="operation-meeting-query__fields min-w-0 flex-1 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-[repeat(2,minmax(0,320px))]"
         >
-          <el-form-item label="所属公司/项目部" class="operation-meeting-query__item mb-0! min-w-0">
+          <el-form-item label="所属公司" class="operation-meeting-query__item mb-0! min-w-0">
             <el-select
               v-model="query.deptId"
               class="w-full!"
@@ -152,9 +154,11 @@ function handleView(id: number) {
         </div>
       </div>
     </el-form>
-    <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4">
-      <div class="flex-1 relative">
-        <el-auto-resizer class="absolute">
+    <div
+      class="operation-meeting-data-panel bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-h-0"
+    >
+      <div class="flex-1 min-h-0 relative">
+        <el-auto-resizer class="operation-meeting-table-view absolute">
           <template #default="{ width, height }">
             <zm-table
               :data="list"
@@ -164,15 +168,13 @@ function handleView(id: number) {
               :height="height"
               show-border
             >
-              <ZmTableColumn label="会议期次" prop="meetingSeries" width="120" />
+              <ZmTableColumn label="会议期次" prop="meetingSeries" width="180" />
               <ZmTableColumn label="公司名称" prop="companyName" :min-width="220" />
               <ZmTableColumn
                 label="会议日期"
                 prop="meetingDate"
                 cover-formatter
-                :real-value="
-                  (row: OperationMeetingListItem) => dayjs(row.meetingDate).format('YYYY-MM-DD')
-                "
+                :real-value="formatMeetingDate"
                 :min-width="160"
               />
               <ZmTableColumn label="操作" width="120" fixed="right">
@@ -188,9 +190,44 @@ function handleView(id: number) {
             </zm-table>
           </template>
         </el-auto-resizer>
+
+        <div v-loading="loading" class="operation-meeting-card-view">
+          <template v-if="list.length">
+            <article v-for="item in list" :key="item.id" class="operation-meeting-card">
+              <div class="operation-meeting-card__header">
+                <div class="operation-meeting-card__title">
+                  <span>会议期次</span>
+                  <strong>{{ item.meetingSeries || '-' }}</strong>
+                </div>
+              </div>
+
+              <div class="operation-meeting-card__content">
+                <div class="operation-meeting-card__field">
+                  <span>公司名称</span>
+                  <strong>{{ item.companyName || '-' }}</strong>
+                </div>
+                <div class="operation-meeting-card__field">
+                  <span>会议日期</span>
+                  <strong>{{ formatMeetingDate(item) }}</strong>
+                </div>
+              </div>
+
+              <div class="operation-meeting-card__actions">
+                <el-button size="default" link type="primary" @click="handleEdit(item.id)">
+                  编辑
+                </el-button>
+                <el-button size="default" link type="success" @click="handleView(item.id)">
+                  查看
+                </el-button>
+              </div>
+            </article>
+          </template>
+          <el-empty v-else description="暂无数据" :image-size="80" />
+        </div>
       </div>
-      <div class="h-8 mt-2 flex items-center justify-end">
+      <div class="operation-meeting-pagination h-8 mt-2 flex items-center justify-end">
         <el-pagination
+          class="operation-meeting-pagination__desktop"
           size="default"
           v-show="total > 0"
           :current-page="query.pageNo"
@@ -202,6 +239,17 @@ function handleView(id: number) {
           @size-change="handleSizeChange"
           @current-change="handleCurrentChange"
         />
+        <el-pagination
+          class="operation-meeting-pagination__mobile"
+          size="small"
+          v-show="total > 0"
+          :current-page="query.pageNo"
+          :page-size="query.pageSize"
+          :background="true"
+          :total="total"
+          layout="prev, pager, next"
+          @current-change="handleCurrentChange"
+        />
       </div>
     </div>
   </div>
@@ -256,6 +304,88 @@ function handleView(id: number) {
   flex-shrink: 0;
 }
 
+.operation-meeting-table-view {
+  display: block;
+}
+
+.operation-meeting-card-view {
+  display: none;
+}
+
+.operation-meeting-pagination__mobile {
+  display: none;
+}
+
+.operation-meeting-card {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  padding: 12px;
+  padding-bottom: 4px;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgb(15 23 42 / 6%);
+}
+
+.operation-meeting-card__header,
+.operation-meeting-card__actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.operation-meeting-card__title {
+  display: flex;
+  min-width: 0;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.operation-meeting-card__title span,
+.operation-meeting-card__field span {
+  font-size: 12px;
+  line-height: 1.4;
+  color: var(--el-text-color-secondary);
+}
+
+.operation-meeting-card__title strong {
+  overflow: hidden;
+  font-size: 16px;
+  line-height: 1.35;
+  color: var(--el-text-color-primary);
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.operation-meeting-card__content {
+  display: grid;
+  gap: 10px;
+}
+
+.operation-meeting-card__field {
+  display: grid;
+  grid-template-columns: 72px minmax(0, 1fr);
+  gap: 12px;
+  align-items: start;
+}
+
+.operation-meeting-card__field strong {
+  min-width: 0;
+  font-size: 13px;
+  font-weight: 600;
+  line-height: 1.5;
+  color: var(--el-text-color-primary);
+  overflow-wrap: anywhere;
+}
+
+.operation-meeting-card__actions {
+  justify-content: flex-end;
+  padding-top: 2px;
+  border-top: 1px solid var(--el-border-color-lighter);
+}
+
 @media (width >= 640px) {
   .operation-meeting-query__fields {
     width: min(100%, 664px);
@@ -266,4 +396,47 @@ function handleView(id: number) {
     max-width: none;
   }
 }
+
+@media (width < 768px) {
+  .operation-meeting-page {
+    height: auto;
+    min-height: calc(
+      100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+    );
+    grid-template-rows: auto 1fr;
+    gap: 12px;
+  }
+
+  .operation-meeting-data-panel {
+    min-height: 280px;
+    padding: 12px;
+  }
+
+  .operation-meeting-table-view,
+  .operation-meeting-pagination__desktop {
+    display: none;
+  }
+
+  .operation-meeting-card-view {
+    display: flex;
+    min-height: 180px;
+    flex: 1;
+    flex-direction: column;
+    gap: 12px;
+    overflow-y: auto;
+  }
+
+  .operation-meeting-pagination {
+    height: auto;
+    min-height: 32px;
+    justify-content: center;
+    margin-top: 12px;
+  }
+
+  .operation-meeting-pagination__mobile {
+    display: flex;
+    max-width: 100%;
+    justify-content: center;
+  }
+}
 </style>

+ 346 - 66
src/views/pms/operation-meeting/meeting-form.vue

@@ -3,6 +3,7 @@ import type { FormInstance, FormRules } from 'element-plus'
 import type { DeptOption, DetailItem, OperationMeeting } from './types'
 import { OperationMeetingApi } from '@/api/pms/meeting'
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+import { useWindowSize } from '@vueuse/core'
 
 interface Props {
   visible: boolean
@@ -37,6 +38,7 @@ const message = useMessage()
 const operationMeeting = ref<OperationMeetingForm>({})
 const operationMeetingRef = ref<FormInstance>()
 const loading = ref(false)
+const { width } = useWindowSize()
 
 const detailItems = ref<DetailItem[]>([])
 const { ZmTable, ZmTableColumn } = useTableComponents<DetailItem>()
@@ -49,13 +51,19 @@ const drawerTitle = computed(() =>
   props.type === 'create' ? '创建会议' : props.type === 'edit' ? '编辑会议' : '查看会议'
 )
 
+const isMobile = computed(() => width.value <= 768)
+const drawerSize = computed(() => (isMobile.value ? '100%' : '92.2%'))
+const detailDrawerSize = computed(() => (isMobile.value ? '100%' : '50%'))
+
 const companyDisplayName = computed(() => {
   if (operationMeeting.value.companyName) {
     return operationMeeting.value.companyName
   }
 
   if (operationMeeting.value.deptId) {
-    return props.deptOptions.find((item) => item.value === operationMeeting.value.deptId)?.label || ''
+    return (
+      props.deptOptions.find((item) => item.value === operationMeeting.value.deptId)?.label || ''
+    )
   }
 
   return ''
@@ -143,6 +151,60 @@ const detailSummaryFields = [
   'cumulativeOnAccount',
   'currentPayment',
   'cumulativePayment'
+] as const
+
+type DetailSummaryField = (typeof detailSummaryFields)[number]
+
+interface DetailCardField {
+  label: string
+  prop: keyof DetailItem
+  unit?: string
+  numeric?: boolean
+}
+
+const detailSummaryLabelMap: Record<DetailSummaryField, string> = {
+  currentRevenue: '收入-本期',
+  cumulativeRevenue: '收入-累计',
+  currentOnAccount: '挂帐-本期',
+  cumulativeOnAccount: '挂帐-累计',
+  currentPayment: '回款-本期',
+  cumulativePayment: '回款-累计'
+}
+
+const detailCardGroups: { title: string; fields: DetailCardField[] }[] = [
+  {
+    title: '经营情况',
+    fields: [
+      { label: '收入-本期', prop: 'currentRevenue', unit: '万元', numeric: true },
+      { label: '收入-累计', prop: 'cumulativeRevenue', unit: '万元', numeric: true },
+      { label: '挂帐-本期', prop: 'currentOnAccount', unit: '万元', numeric: true },
+      { label: '挂帐-累计', prop: 'cumulativeOnAccount', unit: '万元', numeric: true },
+      { label: '回款-本期', prop: 'currentPayment', unit: '万元', numeric: true },
+      { label: '回款-累计', prop: 'cumulativePayment', unit: '万元', numeric: true }
+    ]
+  },
+  {
+    title: '本期生产运行情况',
+    fields: [
+      { label: '计划工作量', prop: 'plannedWorkload' },
+      { label: '实际完成', prop: 'actualCompletion' },
+      { label: '设备利用率', prop: 'equipmentUtilizationRate', unit: '%', numeric: true }
+    ]
+  },
+  {
+    title: '生产管理情况及重点工作',
+    fields: [
+      { label: '重点工作及完成情况', prop: 'keyWorkCompletion' },
+      { label: '存在问题及分析', prop: 'problemsAnalysis' }
+    ]
+  },
+  {
+    title: '下期工作计划',
+    fields: [
+      { label: '计划工作量', prop: 'nextPlannedWorkload' },
+      { label: '重点工作事项', prop: 'priorityTasks' }
+    ]
+  }
 ]
 
 const requiredTextRule = (message: string) => [
@@ -205,6 +267,36 @@ const formatSummaryNumber = (value: number) =>
     minimumFractionDigits: Number.isInteger(value) ? 0 : 2
   })
 
+const getDetailSummaryTotal = (field: DetailSummaryField) =>
+  detailItems.value.reduce((sum, item) => sum + Number(item[field] || 0), 0)
+
+const detailSummaryCards = computed(() =>
+  detailSummaryFields.map((field) => ({
+    label: detailSummaryLabelMap[field],
+    value: formatSummaryNumber(getDetailSummaryTotal(field))
+  }))
+)
+
+const formatDetailCardValue = (item: DetailItem, field: DetailCardField) => {
+  const value = item[field.prop]
+
+  if (value === undefined || value === null || value === '') {
+    return '-'
+  }
+
+  if (!field.numeric) {
+    return String(value)
+  }
+
+  const numericValue = Number(value)
+
+  if (Number.isNaN(numericValue)) {
+    return '-'
+  }
+
+  return `${formatSummaryNumber(numericValue)}${field.unit || ''}`
+}
+
 const getDetailSummaries = ({ columns, data }: DetailSummaryMethodProps) => {
   const sums: string[] = []
 
@@ -214,7 +306,7 @@ const getDetailSummaries = ({ columns, data }: DetailSummaryMethodProps) => {
       return
     }
 
-    if (!column.property || !detailSummaryFields.includes(column.property)) {
+    if (!column.property || !detailSummaryFields.includes(column.property as DetailSummaryField)) {
       sums[index] = ''
       return
     }
@@ -399,7 +491,7 @@ const saveDetailItem = async () => {
     header-class="mb-0! p-4!"
     body-class="bg-gray-100"
     footer-class="p-4!"
-    size="92.2%"
+    :size="drawerSize"
   >
     <template #header>
       <div class="flex items-center">
@@ -422,10 +514,7 @@ const saveDetailItem = async () => {
         <h3 class="text-lg font-bold mb-4">会议信息</h3>
 
         <div class="meeting-section__grid">
-          <el-form-item
-            label="所属公司/项目部"
-            class="meeting-form-item mb-0! min-w-0"
-          >
+          <el-form-item label="所属公司" class="meeting-form-item mb-0! min-w-0">
             <el-input
               :model-value="companyDisplayName"
               class="w-full!"
@@ -435,11 +524,7 @@ const saveDetailItem = async () => {
             <div class="meeting-form-tip">新建保存后由系统自动填充。</div>
           </el-form-item>
 
-          <el-form-item
-            label="会议日期"
-            prop="meetingDate"
-            class="meeting-form-item mb-0! min-w-0"
-          >
+          <el-form-item label="会议日期" prop="meetingDate" class="meeting-form-item mb-0! min-w-0">
             <el-date-picker
               v-model="operationMeeting.meetingDate"
               type="date"
@@ -486,60 +571,129 @@ const saveDetailItem = async () => {
             新增一行
           </el-button>
         </div>
-        <ZmTable
-          :data="detailItems"
-          :loading="loading"
-          show-summary
-          :summary-method="getDetailSummaries"
-        >
-          <zm-table-column label="项目部" prop="projectName" />
-          <zm-table-column label="收入(万元)">
-            <zm-table-column label="本期" prop="currentRevenue" />
-            <zm-table-column label="累计" prop="cumulativeRevenue" />
-          </zm-table-column>
-          <zm-table-column label="挂帐(万元)">
-            <zm-table-column label="本期" prop="currentOnAccount" />
-            <zm-table-column label="累计" prop="cumulativeOnAccount" />
-          </zm-table-column>
-          <zm-table-column label="回款(万元)">
-            <zm-table-column label="本期" prop="currentPayment" />
-            <zm-table-column label="累计" prop="cumulativePayment" />
-          </zm-table-column>
-          <zm-table-column label="本期生产运行情况">
-            <zm-table-column label="计划工作量" prop="plannedWorkload" />
-            <zm-table-column label="实际完成" prop="actualCompletion" />
-            <zm-table-column label="设备利用率" prop="equipmentUtilizationRate" />
-          </zm-table-column>
-          <zm-table-column label="生产管理情况及重点工作	">
-            <zm-table-column label="重点工作及完成情况" prop="keyWorkCompletion" />
-            <zm-table-column label="存在问题及分析" prop="problemsAnalysis" />
-          </zm-table-column>
-          <zm-table-column label="下期工作计划		">
-            <zm-table-column label="计划工作量" prop="nextPlannedWorkload" />
-            <zm-table-column label="重点工作事项" prop="priorityTasks" />
-          </zm-table-column>
-          <zm-table-column label="操作" width="120" fixed="right">
-            <template #default="{ row, $index }">
-              <el-button
-                link
-                size="small"
-                type="primary"
-                @click="handleEditDetailItem(row, $index)"
-              >
-                {{ type === 'view' ? '查看' : '编辑' }}
-              </el-button>
-              <el-button
-                v-if="type !== 'view'"
-                link
-                size="small"
-                type="danger"
-                @click="handleDeleteDetailItem($index)"
+        <div class="meeting-detail-table-view">
+          <ZmTable
+            :data="detailItems"
+            :loading="loading"
+            show-summary
+            :summary-method="getDetailSummaries"
+          >
+            <zm-table-column label="项目部" prop="projectName" />
+            <zm-table-column label="收入(万元)">
+              <zm-table-column label="本期" prop="currentRevenue" />
+              <zm-table-column label="累计" prop="cumulativeRevenue" />
+            </zm-table-column>
+            <zm-table-column label="挂帐(万元)">
+              <zm-table-column label="本期" prop="currentOnAccount" />
+              <zm-table-column label="累计" prop="cumulativeOnAccount" />
+            </zm-table-column>
+            <zm-table-column label="回款(万元)">
+              <zm-table-column label="本期" prop="currentPayment" />
+              <zm-table-column label="累计" prop="cumulativePayment" />
+            </zm-table-column>
+            <zm-table-column label="本期生产运行情况">
+              <zm-table-column label="计划工作量" prop="plannedWorkload" />
+              <zm-table-column label="实际完成" prop="actualCompletion" />
+              <zm-table-column label="设备利用率" prop="equipmentUtilizationRate" />
+            </zm-table-column>
+            <zm-table-column label="生产管理情况及重点工作	">
+              <zm-table-column label="重点工作及完成情况" prop="keyWorkCompletion" />
+              <zm-table-column label="存在问题及分析" prop="problemsAnalysis" />
+            </zm-table-column>
+            <zm-table-column label="下期工作计划		">
+              <zm-table-column label="计划工作量" prop="nextPlannedWorkload" />
+              <zm-table-column label="重点工作事项" prop="priorityTasks" />
+            </zm-table-column>
+            <zm-table-column label="操作" width="120" fixed="right">
+              <template #default="{ row, $index }">
+                <el-button
+                  link
+                  size="small"
+                  type="primary"
+                  @click="handleEditDetailItem(row, $index)"
+                >
+                  {{ type === 'view' ? '查看' : '编辑' }}
+                </el-button>
+                <el-button
+                  v-if="type !== 'view'"
+                  link
+                  size="small"
+                  type="danger"
+                  @click="handleDeleteDetailItem($index)"
+                >
+                  删除
+                </el-button>
+              </template>
+            </zm-table-column>
+          </ZmTable>
+        </div>
+
+        <div v-loading="loading" class="meeting-detail-card-view">
+          <template v-if="detailItems.length">
+            <article v-for="(item, index) in detailItems" :key="index" class="meeting-detail-card">
+              <div class="meeting-detail-card__header">
+                <div class="meeting-detail-card__title">
+                  <span>项目部</span>
+                  <strong>{{ item.projectName || '-' }}</strong>
+                </div>
+                <el-tag size="small" effect="plain">第 {{ index + 1 }} 项</el-tag>
+              </div>
+
+              <section
+                v-for="group in detailCardGroups"
+                :key="group.title"
+                class="meeting-detail-card__group"
               >
-                删除
-              </el-button>
-            </template>
-          </zm-table-column>
-        </ZmTable>
+                <h4>{{ group.title }}</h4>
+                <div class="meeting-detail-card__fields">
+                  <div
+                    v-for="field in group.fields"
+                    :key="field.prop"
+                    class="meeting-detail-card__field"
+                  >
+                    <span>{{ field.label }}</span>
+                    <strong>{{ formatDetailCardValue(item, field) }}</strong>
+                  </div>
+                </div>
+              </section>
+
+              <div class="meeting-detail-card__actions">
+                <el-button
+                  link
+                  size="small"
+                  type="primary"
+                  @click="handleEditDetailItem(item, index)"
+                >
+                  {{ type === 'view' ? '查看' : '编辑' }}
+                </el-button>
+                <el-button
+                  v-if="type !== 'view'"
+                  link
+                  size="small"
+                  type="danger"
+                  @click="handleDeleteDetailItem(index)"
+                >
+                  删除
+                </el-button>
+              </div>
+            </article>
+
+            <section class="meeting-detail-summary-card">
+              <div class="meeting-detail-summary-card__title">公司整体</div>
+              <div class="meeting-detail-summary-card__grid">
+                <div
+                  v-for="item in detailSummaryCards"
+                  :key="item.label"
+                  class="meeting-detail-summary-card__item"
+                >
+                  <span>{{ item.label }}</span>
+                  <strong>{{ item.value }}万元</strong>
+                </div>
+              </div>
+            </section>
+          </template>
+          <el-empty v-else description="暂无会议明细" :image-size="80" />
+        </div>
       </section>
     </el-form>
 
@@ -560,7 +714,7 @@ const saveDetailItem = async () => {
       :model-value="detailDrawerVisible"
       @update:model-value="handleDetailDrawerChange"
       :append-to-body="true"
-      size="50%"
+      :size="detailDrawerSize"
       :close-on-click-modal="false"
       :close-on-press-escape="false"
       :show-close="false"
@@ -787,6 +941,116 @@ const saveDetailItem = async () => {
   grid-template-columns: 1fr;
 }
 
+.meeting-detail-table-view {
+  display: block;
+}
+
+.meeting-detail-card-view {
+  display: none;
+}
+
+.meeting-detail-card {
+  display: flex;
+  flex-direction: column;
+  gap: 14px;
+  padding: 12px;
+  padding-bottom: 4px;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgb(15 23 42 / 6%);
+}
+
+.meeting-detail-card__header,
+.meeting-detail-card__actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.meeting-detail-card__title {
+  display: flex;
+  min-width: 0;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.meeting-detail-card__title span,
+.meeting-detail-card__field span,
+.meeting-detail-summary-card__item span {
+  font-size: 12px;
+  line-height: 1.4;
+  color: var(--el-text-color-secondary);
+}
+
+.meeting-detail-card__title strong {
+  overflow: hidden;
+  font-size: 16px;
+  line-height: 1.35;
+  color: var(--el-text-color-primary);
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.meeting-detail-card__group {
+  padding-top: 12px;
+  border-top: 1px solid var(--el-border-color-lighter);
+}
+
+.meeting-detail-card__group h4 {
+  margin: 0 0 10px;
+  font-size: 14px;
+  font-weight: 700;
+  color: var(--el-text-color-primary);
+}
+
+.meeting-detail-card__fields,
+.meeting-detail-summary-card__grid {
+  display: grid;
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+  gap: 10px 12px;
+}
+
+.meeting-detail-card__field,
+.meeting-detail-summary-card__item {
+  display: flex;
+  min-width: 0;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.meeting-detail-card__field strong,
+.meeting-detail-summary-card__item strong {
+  min-width: 0;
+  font-size: 13px;
+  font-weight: 600;
+  line-height: 1.5;
+  color: var(--el-text-color-primary);
+  overflow-wrap: anywhere;
+  white-space: pre-wrap;
+}
+
+.meeting-detail-card__actions {
+  justify-content: flex-end;
+  padding-top: 2px;
+  border-top: 1px solid var(--el-border-color-lighter);
+}
+
+.meeting-detail-summary-card {
+  padding: 14px;
+  background: var(--el-fill-color-lighter);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+}
+
+.meeting-detail-summary-card__title {
+  margin-bottom: 10px;
+  font-size: 14px;
+  font-weight: 700;
+  color: var(--el-text-color-primary);
+}
+
 @media (width <= 960px) {
   .meeting-section__grid {
     grid-template-columns: 1fr;
@@ -801,5 +1065,21 @@ const saveDetailItem = async () => {
   .meeting-section {
     padding: 16px;
   }
+
+  .meeting-detail-table-view {
+    display: none;
+  }
+
+  .meeting-detail-card-view {
+    display: flex;
+    min-height: 160px;
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .meeting-detail-card__fields,
+  .meeting-detail-summary-card__grid {
+    grid-template-columns: 1fr;
+  }
 }
 </style>

+ 0 - 103
src/views/pms/operation-meeting/meeting-table.vue

@@ -1,103 +0,0 @@
-<script setup lang="ts">
-import { useTableComponents } from '@/components/ZmTable/useTableComponents'
-import type { OperationMeetingListItem } from './types'
-import dayjs from 'dayjs'
-
-const props = defineProps({
-  list: {
-    type: Array as PropType<OperationMeetingListItem[]>,
-    default: () => []
-  },
-  loading: {
-    type: Boolean,
-    default: false
-  },
-  total: {
-    type: Number,
-    default: 0
-  },
-  pageNo: {
-    type: Number,
-    default: 1
-  },
-  pageSize: {
-    type: Number,
-    default: 10
-  },
-  showAction: {
-    type: Boolean,
-    default: true
-  }
-})
-
-const emits = defineEmits(['sizeChange', 'currentChange'])
-
-const { list, loading, total, pageNo, pageSize, showAction } = toRefs(props)
-const { ZmTable, ZmTableColumn } = useTableComponents<OperationMeetingListItem>()
-
-function handleSizeChange(val: number) {
-  emits('sizeChange', val)
-}
-
-function handleCurrentChange(val: number) {
-  emits('currentChange', val)
-}
-</script>
-
-<template>
-  <div
-    class="min-w-0 min-h-0 flex flex-col overflow-hidden rounded-lg bg-white p-4 shadow dark:bg-[#1d1e1f]"
-  >
-    <div class="min-w-0 min-h-0 flex-1 relative overflow-hidden">
-      <el-auto-resizer class="absolute">
-        <template #default="{ width, height }">
-          <ZmTable
-            :data="list"
-            :loading="loading"
-            :width="width"
-            :max-height="height"
-            :height="height"
-            show-border
-          >
-            <ZmTableColumn label="会议期次" prop="session" :min-width="120" />
-            <ZmTableColumn label="公司名称" prop="companyName" :min-width="220" />
-            <ZmTableColumn label="项目数" :min-width="120">
-              <template #default="{ row }">
-                <el-tag type="info" effect="plain">{{ row.projectItems?.length || 0 }} 项</el-tag>
-              </template>
-            </ZmTableColumn>
-            <ZmTableColumn
-              label="会议日期"
-              prop="meetingDate"
-              cover-formatter
-              :real-value="
-                (row: OperationMeetingListItem) => dayjs(row.meetingDate).format('YYYY-MM-DD')
-              "
-              :min-width="160"
-            />
-            <ZmTableColumn label="操作" :width="140" fixed="right" v-if="showAction">
-              <template #default="scope">
-                <slot name="action" :row="scope.row"></slot>
-              </template>
-            </ZmTableColumn>
-          </ZmTable>
-        </template>
-      </el-auto-resizer>
-    </div>
-
-    <div class="mt-2 flex items-center justify-end overflow-x-auto">
-      <el-pagination
-        size="default"
-        v-show="total > 0"
-        :current-page="pageNo"
-        :page-size="pageSize"
-        :background="true"
-        :page-sizes="[10, 20, 30, 50, 100]"
-        :total="total"
-        layout="total, sizes, prev, pager, next, jumper"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-      />
-    </div>
-  </div>
-</template>