|
@@ -1,10 +1,10 @@
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
|
-import type { CSSProperties } from 'vue'
|
|
|
|
|
import type { DeptOption, DetailItem, OperationMeeting } from './types'
|
|
import type { DeptOption, DetailItem, OperationMeeting } from './types'
|
|
|
|
|
+import MeetingDetailDrawer from './components/meeting-detail-drawer.vue'
|
|
|
|
|
+import OperationMeetingContent from './components/operation-meeting-content.vue'
|
|
|
import { OperationMeetingApi } from '@/api/pms/meeting'
|
|
import { OperationMeetingApi } from '@/api/pms/meeting'
|
|
|
-import { useTableComponents } from '@/components/ZmTable/useTableComponents'
|
|
|
|
|
-import { useDebounceFn, useWindowSize } from '@vueuse/core'
|
|
|
|
|
|
|
+import { useWindowSize } from '@vueuse/core'
|
|
|
|
|
|
|
|
interface Props {
|
|
interface Props {
|
|
|
visible: boolean
|
|
visible: boolean
|
|
@@ -33,55 +33,33 @@ const loading = ref(false)
|
|
|
const { width } = useWindowSize()
|
|
const { width } = useWindowSize()
|
|
|
|
|
|
|
|
const detailItems = ref<DetailItem[]>([])
|
|
const detailItems = ref<DetailItem[]>([])
|
|
|
-const { ZmTable, ZmTableColumn } = useTableComponents<DetailItem>()
|
|
|
|
|
const detailDrawerVisible = ref(false)
|
|
const detailDrawerVisible = ref(false)
|
|
|
-const detailFormRef = ref<FormInstance>()
|
|
|
|
|
const detailEditingIndex = ref(-1)
|
|
const detailEditingIndex = ref(-1)
|
|
|
const detailFormType = ref<'create' | 'edit'>('create')
|
|
const detailFormType = ref<'create' | 'edit'>('create')
|
|
|
|
|
+const detailForm = ref<DetailItem>(createDetailItem())
|
|
|
|
|
|
|
|
const isMobile = computed(() => width.value <= 768)
|
|
const isMobile = computed(() => width.value <= 768)
|
|
|
const drawerSize = computed(() => (isMobile.value ? '100%' : '100%'))
|
|
const drawerSize = computed(() => (isMobile.value ? '100%' : '100%'))
|
|
|
-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 || ''
|
|
|
|
|
- )
|
|
|
|
|
|
|
+function createDetailItem(): DetailItem {
|
|
|
|
|
+ return {
|
|
|
|
|
+ raw: {},
|
|
|
|
|
+ projectName: '',
|
|
|
|
|
+ currentRevenue: undefined,
|
|
|
|
|
+ cumulativeRevenue: undefined,
|
|
|
|
|
+ currentOnAccount: undefined,
|
|
|
|
|
+ cumulativeOnAccount: undefined,
|
|
|
|
|
+ currentPayment: undefined,
|
|
|
|
|
+ cumulativePayment: undefined,
|
|
|
|
|
+ plannedWorkload: '',
|
|
|
|
|
+ actualCompletion: '',
|
|
|
|
|
+ equipmentUtilizationRate: undefined,
|
|
|
|
|
+ keyWorkCompletion: '',
|
|
|
|
|
+ problemsAnalysis: '',
|
|
|
|
|
+ nextPlannedWorkload: '',
|
|
|
|
|
+ priorityTasks: ''
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- return ''
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const detailDrawerTitle = computed(() => {
|
|
|
|
|
- if (props.type === 'view') {
|
|
|
|
|
- return '查看会议明细'
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return detailFormType.value === 'create' ? '新增会议明细' : '编辑会议明细'
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const createDetailItem = (): DetailItem => ({
|
|
|
|
|
- raw: {},
|
|
|
|
|
- projectName: '',
|
|
|
|
|
- currentRevenue: undefined,
|
|
|
|
|
- cumulativeRevenue: undefined,
|
|
|
|
|
- currentOnAccount: undefined,
|
|
|
|
|
- cumulativeOnAccount: undefined,
|
|
|
|
|
- currentPayment: undefined,
|
|
|
|
|
- cumulativePayment: undefined,
|
|
|
|
|
- plannedWorkload: '',
|
|
|
|
|
- actualCompletion: '',
|
|
|
|
|
- equipmentUtilizationRate: undefined,
|
|
|
|
|
- keyWorkCompletion: '',
|
|
|
|
|
- problemsAnalysis: '',
|
|
|
|
|
- nextPlannedWorkload: '',
|
|
|
|
|
- priorityTasks: ''
|
|
|
|
|
-})
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
|
|
const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
|
|
|
raw: data?.raw || {},
|
|
raw: data?.raw || {},
|
|
@@ -133,88 +111,6 @@ const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
|
|
|
priorityTasks: String(data?.priorityTasks || '')
|
|
priorityTasks: String(data?.priorityTasks || '')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const detailForm = ref<DetailItem>(createDetailItem())
|
|
|
|
|
-const previousWorkPlanQueryKey = ref('')
|
|
|
|
|
-
|
|
|
|
|
-const detailSummaryFields = [
|
|
|
|
|
- 'currentRevenue',
|
|
|
|
|
- 'cumulativeRevenue',
|
|
|
|
|
- 'currentOnAccount',
|
|
|
|
|
- '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 detailSummaryToneMap: Record<DetailSummaryField, 'revenue' | 'account' | 'payment'> = {
|
|
|
|
|
- currentRevenue: 'revenue',
|
|
|
|
|
- cumulativeRevenue: 'revenue',
|
|
|
|
|
- currentOnAccount: 'account',
|
|
|
|
|
- cumulativeOnAccount: 'account',
|
|
|
|
|
- currentPayment: 'payment',
|
|
|
|
|
- cumulativePayment: 'payment'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-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 meetingTableBlueColumns = new Set<keyof DetailItem>([
|
|
|
|
|
- 'projectName',
|
|
|
|
|
- 'actualCompletion',
|
|
|
|
|
- 'keyWorkCompletion',
|
|
|
|
|
- 'problemsAnalysis'
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
const requiredTextRule = (message: string) => [
|
|
const requiredTextRule = (message: string) => [
|
|
|
{
|
|
{
|
|
|
required: true,
|
|
required: true,
|
|
@@ -224,46 +120,6 @@ const requiredTextRule = (message: string) => [
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
-const requiredNumberRule = (message: string) => [
|
|
|
|
|
- {
|
|
|
|
|
- required: true,
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- message,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-const nonNegativeNumberRule = (requiredMessage: string, fieldLabel: string) => [
|
|
|
|
|
- ...requiredNumberRule(requiredMessage),
|
|
|
|
|
- {
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- min: 0,
|
|
|
|
|
- message: `${fieldLabel}不能小于0`,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-const numberRangeRule = (requiredMessage: string, fieldLabel: string, min: number, max: number) => [
|
|
|
|
|
- ...requiredNumberRule(requiredMessage),
|
|
|
|
|
- {
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- min,
|
|
|
|
|
- max,
|
|
|
|
|
- message: `${fieldLabel}需在${min}到${max}之间`,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-const optionalNumberRangeRule = (fieldLabel: string, min: number, max: number) => [
|
|
|
|
|
- {
|
|
|
|
|
- type: 'number' as const,
|
|
|
|
|
- min,
|
|
|
|
|
- max,
|
|
|
|
|
- message: `${fieldLabel}需在${min}到${max}之间`,
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
const operationMeetingRules = reactive<FormRules>({
|
|
const operationMeetingRules = reactive<FormRules>({
|
|
|
meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }],
|
|
meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }],
|
|
|
meetingSeries: [
|
|
meetingSeries: [
|
|
@@ -283,109 +139,18 @@ const operationMeetingRules = reactive<FormRules>({
|
|
|
support: requiredTextRule('请输入需集团协调支持的事项')
|
|
support: requiredTextRule('请输入需集团协调支持的事项')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const equipmentUtilizationRateRequired = ref(true)
|
|
|
|
|
-const getEquipmentUtilizationRateRules = () =>
|
|
|
|
|
- equipmentUtilizationRateRequired.value
|
|
|
|
|
- ? numberRangeRule('请输入设备利用率', '设备利用率', 0, 100)
|
|
|
|
|
- : optionalNumberRangeRule('设备利用率', 0, 100)
|
|
|
|
|
-
|
|
|
|
|
-const detailRules = reactive<FormRules>({
|
|
|
|
|
- projectName: requiredTextRule('请选择或者输入项目名称'),
|
|
|
|
|
- currentRevenue: nonNegativeNumberRule('请输入收入-本期', '收入-本期'),
|
|
|
|
|
- cumulativeRevenue: nonNegativeNumberRule('请输入收入-累计', '收入-累计'),
|
|
|
|
|
- currentOnAccount: nonNegativeNumberRule('请输入挂帐-本期', '挂帐-本期'),
|
|
|
|
|
- cumulativeOnAccount: nonNegativeNumberRule('请输入挂帐-累计', '挂帐-累计'),
|
|
|
|
|
- currentPayment: nonNegativeNumberRule('请输入回款-本期', '回款-本期'),
|
|
|
|
|
- cumulativePayment: nonNegativeNumberRule('请输入回款-累计', '回款-累计'),
|
|
|
|
|
- plannedWorkload: requiredTextRule('请输入计划工作量'),
|
|
|
|
|
- actualCompletion: requiredTextRule('请输入实际完成'),
|
|
|
|
|
- equipmentUtilizationRate: getEquipmentUtilizationRateRules(),
|
|
|
|
|
- keyWorkCompletion: requiredTextRule('请输入重点工作及完成情况'),
|
|
|
|
|
- problemsAnalysis: requiredTextRule('请输入存在问题及分析'),
|
|
|
|
|
- nextPlannedWorkload: requiredTextRule('请输入下期计划工作量'),
|
|
|
|
|
- priorityTasks: requiredTextRule('请输入重点工作事项')
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const updateEquipmentUtilizationRateRule = (required: boolean) => {
|
|
|
|
|
- equipmentUtilizationRateRequired.value = required
|
|
|
|
|
- detailRules.equipmentUtilizationRate = getEquipmentUtilizationRateRules()
|
|
|
|
|
- nextTick(() => detailFormRef.value?.clearValidate('equipmentUtilizationRate'))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const loadEquipmentUtilizationRateRule = async () => {
|
|
|
|
|
- updateEquipmentUtilizationRateRule(true)
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const data = await OperationMeetingApi.getMandatoryOrNot()
|
|
|
|
|
- updateEquipmentUtilizationRateRule(data?.mandatory !== false)
|
|
|
|
|
- } catch {
|
|
|
|
|
- updateEquipmentUtilizationRateRule(true)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const formatSummaryNumber = (value: number) =>
|
|
|
|
|
- value.toLocaleString('zh-CN', {
|
|
|
|
|
- maximumFractionDigits: 2,
|
|
|
|
|
- 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)),
|
|
|
|
|
- tone: detailSummaryToneMap[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 getMeetingTableCellStyle: any = ({ column }): CSSProperties | undefined => {
|
|
|
|
|
- const property = column.property as keyof DetailItem | undefined
|
|
|
|
|
-
|
|
|
|
|
- if (property && meetingTableBlueColumns.has(property)) {
|
|
|
|
|
- return { color: '#1b71f6' }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return undefined
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
const handleAddDetailItem = () => {
|
|
const handleAddDetailItem = () => {
|
|
|
detailFormType.value = 'create'
|
|
detailFormType.value = 'create'
|
|
|
detailEditingIndex.value = -1
|
|
detailEditingIndex.value = -1
|
|
|
detailForm.value = createDetailItem()
|
|
detailForm.value = createDetailItem()
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
detailDrawerVisible.value = true
|
|
detailDrawerVisible.value = true
|
|
|
- nextTick(() => detailFormRef.value?.clearValidate())
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleEditDetailItem = (row: DetailItem, index: number) => {
|
|
const handleEditDetailItem = (row: DetailItem, index: number) => {
|
|
|
detailFormType.value = 'edit'
|
|
detailFormType.value = 'edit'
|
|
|
detailEditingIndex.value = index
|
|
detailEditingIndex.value = index
|
|
|
detailForm.value = cloneDetailItem(row)
|
|
detailForm.value = cloneDetailItem(row)
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
detailDrawerVisible.value = true
|
|
detailDrawerVisible.value = true
|
|
|
- nextTick(() => detailFormRef.value?.clearValidate())
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleDeleteDetailItem = (index: number) => {
|
|
const handleDeleteDetailItem = (index: number) => {
|
|
@@ -399,11 +164,25 @@ const handleDetailDrawerChange = (visible: boolean) => {
|
|
|
detailForm.value = createDetailItem()
|
|
detailForm.value = createDetailItem()
|
|
|
detailFormType.value = 'create'
|
|
detailFormType.value = 'create'
|
|
|
detailEditingIndex.value = -1
|
|
detailEditingIndex.value = -1
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
- nextTick(() => detailFormRef.value?.clearValidate())
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const updateOperationMeeting = (nextOperationMeeting: OperationMeetingForm) => {
|
|
|
|
|
+ operationMeeting.value = nextOperationMeeting
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const saveDetailItem = (item: DetailItem) => {
|
|
|
|
|
+ const nextItem = cloneDetailItem(item)
|
|
|
|
|
+
|
|
|
|
|
+ if (detailEditingIndex.value > -1) {
|
|
|
|
|
+ detailItems.value.splice(detailEditingIndex.value, 1, nextItem)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ detailItems.value.push(nextItem)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ handleDetailDrawerChange(false)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const resetForm = () => {
|
|
const resetForm = () => {
|
|
|
operationMeeting.value = {}
|
|
operationMeeting.value = {}
|
|
|
detailItems.value = []
|
|
detailItems.value = []
|
|
@@ -519,110 +298,6 @@ const submitForm = async () => {
|
|
|
loading.value = false
|
|
loading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-const saveDetailItem = async () => {
|
|
|
|
|
- if (props.type === 'view' || !detailFormRef.value) return
|
|
|
|
|
-
|
|
|
|
|
- const valid = await detailFormRef.value.validate().catch(() => false)
|
|
|
|
|
-
|
|
|
|
|
- if (!valid) return
|
|
|
|
|
-
|
|
|
|
|
- const nextItem = cloneDetailItem(detailForm.value)
|
|
|
|
|
-
|
|
|
|
|
- if (detailEditingIndex.value > -1) {
|
|
|
|
|
- detailItems.value.splice(detailEditingIndex.value, 1, nextItem)
|
|
|
|
|
- } else {
|
|
|
|
|
- detailItems.value.push(nextItem)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- handleDetailDrawerChange(false)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-interface ProjectNameSuggestion {
|
|
|
|
|
- value: string
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const projectNameOptions = ref<string[]>([])
|
|
|
|
|
-const projectNameSuggestions = computed<ProjectNameSuggestion[]>(() =>
|
|
|
|
|
- projectNameOptions.value.map((item) => ({ value: item }))
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-const queryProjectNameSearch = (
|
|
|
|
|
- queryString: string,
|
|
|
|
|
- cb: (results: ProjectNameSuggestion[]) => void
|
|
|
|
|
-) => {
|
|
|
|
|
- const keyword = queryString.trim().toLowerCase()
|
|
|
|
|
-
|
|
|
|
|
- if (!keyword) {
|
|
|
|
|
- cb(projectNameSuggestions.value)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- cb(projectNameSuggestions.value.filter((item) => item.value.toLowerCase().includes(keyword)))
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const getDetailRawQueryValue = (key: 'id' | 'meetingId') => {
|
|
|
|
|
- const value = detailForm.value.raw?.[key]
|
|
|
|
|
-
|
|
|
|
|
- if (value === undefined || value === null) {
|
|
|
|
|
- return ''
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return typeof value === 'string' || typeof value === 'number' ? value : String(value)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const updatePlannedWorkloadFromPreviousPlan = (data?: Record<string, unknown>) => {
|
|
|
|
|
- detailForm.value.plannedWorkload = String(data?.nextPlannedWorkload || '')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const loadPreviousWorkPlan = async () => {
|
|
|
|
|
- const projectName = detailForm.value.projectName.trim()
|
|
|
|
|
-
|
|
|
|
|
- if (!projectName) {
|
|
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const params = {
|
|
|
|
|
- projectName,
|
|
|
|
|
- id: getDetailRawQueryValue('id'),
|
|
|
|
|
- meetingId: getDetailRawQueryValue('meetingId')
|
|
|
|
|
- }
|
|
|
|
|
- const queryKey = JSON.stringify(params)
|
|
|
|
|
-
|
|
|
|
|
- if (previousWorkPlanQueryKey.value === queryKey) return
|
|
|
|
|
-
|
|
|
|
|
- previousWorkPlanQueryKey.value = queryKey
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const data = await OperationMeetingApi.getPreviousWorkPlan(params)
|
|
|
|
|
- if (detailForm.value.projectName.trim() !== projectName) return
|
|
|
|
|
- updatePlannedWorkloadFromPreviousPlan(data)
|
|
|
|
|
- } catch {
|
|
|
|
|
- previousWorkPlanQueryKey.value = ''
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const handleProjectNameComplete = useDebounceFn(loadPreviousWorkPlan, 300)
|
|
|
|
|
-
|
|
|
|
|
-const handleProjectNameSelect = (item: ProjectNameSuggestion) => {
|
|
|
|
|
- detailForm.value.projectName = item.value
|
|
|
|
|
- void handleProjectNameComplete()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function loadProjectNameOptions() {
|
|
|
|
|
- try {
|
|
|
|
|
- const data = await OperationMeetingApi.getProjectNameOptions()
|
|
|
|
|
- projectNameOptions.value = Array.isArray(data) ? data.filter(Boolean).map(String) : []
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- projectNameOptions.value = []
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- loadProjectNameOptions()
|
|
|
|
|
- loadEquipmentUtilizationRateRule()
|
|
|
|
|
-})
|
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
@@ -644,204 +319,18 @@ onMounted(() => {
|
|
|
v-loading="loading"
|
|
v-loading="loading"
|
|
|
scroll-to-error
|
|
scroll-to-error
|
|
|
require-asterisk-position="right"
|
|
require-asterisk-position="right"
|
|
|
- :disabled="type === 'view'"
|
|
|
|
|
>
|
|
>
|
|
|
- <section
|
|
|
|
|
- class="py-2.5 px-4 bg-white border-solid border-1 border-gray-200/90 rounded-xl mb-4"
|
|
|
|
|
- >
|
|
|
|
|
- <div class="meeting-section__grid">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- label="会议期次"
|
|
|
|
|
- label-position="left"
|
|
|
|
|
- prop="meetingSeries"
|
|
|
|
|
- class="mb-0! min-w-0"
|
|
|
|
|
- >
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="operationMeeting.meetingSeries"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- placeholder="请输入会议期次"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :min="1"
|
|
|
|
|
- :step="1"
|
|
|
|
|
- :precision="0"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item label="所属公司" label-position="left" class="mb-0! min-w-0">
|
|
|
|
|
- <el-input
|
|
|
|
|
- :model-value="companyDisplayName"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- placeholder="新建保存后系统自动填充"
|
|
|
|
|
- disabled
|
|
|
|
|
- />
|
|
|
|
|
- <div v-if="type === 'create'" class="absolute text-xs text-gray top-8">
|
|
|
|
|
- 新建保存后由系统自动填充。
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- label="会议日期"
|
|
|
|
|
- label-position="left"
|
|
|
|
|
- prop="meetingDate"
|
|
|
|
|
- class="mb-0! min-w-0"
|
|
|
|
|
- >
|
|
|
|
|
- <el-date-picker
|
|
|
|
|
- v-model="operationMeeting.meetingDate"
|
|
|
|
|
- type="date"
|
|
|
|
|
- placeholder="请选择会议日期"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <section class="meeting-summary-strip meeting-section__grid-full">
|
|
|
|
|
- <div class="meeting-summary-strip__title">
|
|
|
|
|
- <span>公司整体</span>
|
|
|
|
|
- <small>经营数据汇总</small>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="meeting-summary-strip__grid">
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="item in detailSummaryCards"
|
|
|
|
|
- :key="item.label"
|
|
|
|
|
- :class="[
|
|
|
|
|
- 'meeting-summary-strip__item',
|
|
|
|
|
- `meeting-summary-strip__item--${item.tone}`
|
|
|
|
|
- ]"
|
|
|
|
|
- >
|
|
|
|
|
- <span>{{ item.label }}</span>
|
|
|
|
|
- <strong>{{ item.value }}<em>万元</em></strong>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
- <section class="p-6 bg-white border-solid border-1 border-gray-200/90 rounded-xl">
|
|
|
|
|
- <div class="flex items-center justify-between gap-4 mb-4">
|
|
|
|
|
- <h3 class="text-lg font-bold m-0">会议明细</h3>
|
|
|
|
|
- <el-button v-if="type !== 'view'" type="primary" @click="handleAddDetailItem">
|
|
|
|
|
- <Icon icon="ep:plus" class="mr-5px" />
|
|
|
|
|
- 新增一行
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="meeting-detail-table-view">
|
|
|
|
|
- <ZmTable
|
|
|
|
|
- class="meeting-table"
|
|
|
|
|
- :data="detailItems"
|
|
|
|
|
- :loading="loading"
|
|
|
|
|
- :max-height="660"
|
|
|
|
|
- align="left"
|
|
|
|
|
- :show-overflow-tooltip="false"
|
|
|
|
|
- :cell-style="getMeetingTableCellStyle"
|
|
|
|
|
- >
|
|
|
|
|
- <zm-table-column width="156" align="center" label="项目名称" prop="projectName" />
|
|
|
|
|
- <zm-table-column label="本期生产运行情况">
|
|
|
|
|
- <zm-table-column width="220" label="计划工作量" prop="plannedWorkload" />
|
|
|
|
|
- <zm-table-column width="220" label="实际完成" prop="actualCompletion" />
|
|
|
|
|
- <zm-table-column
|
|
|
|
|
- width="120"
|
|
|
|
|
- label="设备利用率"
|
|
|
|
|
- prop="equipmentUtilizationRate"
|
|
|
|
|
- :formatter="(row) => `${row.equipmentUtilizationRate}%`"
|
|
|
|
|
- />
|
|
|
|
|
- </zm-table-column>
|
|
|
|
|
- <zm-table-column label="生产管理情况及重点工作 ">
|
|
|
|
|
- <zm-table-column label="重点工作及完成情况" prop="keyWorkCompletion" />
|
|
|
|
|
- <zm-table-column width="300" label="存在问题及分析" prop="problemsAnalysis" />
|
|
|
|
|
- </zm-table-column>
|
|
|
|
|
- <zm-table-column label="下期工作计划 ">
|
|
|
|
|
- <zm-table-column width="240" label="计划工作量" prop="nextPlannedWorkload" />
|
|
|
|
|
- <zm-table-column label="重点工作事项" prop="priorityTasks" />
|
|
|
|
|
- </zm-table-column>
|
|
|
|
|
- <zm-table-column label="操作" width="140" fixed="right">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-button
|
|
|
|
|
- link
|
|
|
|
|
- size="default"
|
|
|
|
|
- type="primary"
|
|
|
|
|
- @click="handleEditDetailItem(row, $index)"
|
|
|
|
|
- >
|
|
|
|
|
- {{ type === 'view' ? '查看' : '编辑' }}
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <el-button
|
|
|
|
|
- v-if="type !== 'view'"
|
|
|
|
|
- link
|
|
|
|
|
- size="default"
|
|
|
|
|
- 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"
|
|
|
|
|
- >
|
|
|
|
|
- <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>
|
|
|
|
|
- </template>
|
|
|
|
|
- <el-empty v-else description="暂无会议明细" :image-size="80" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <section class="meeting-support-panel">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- label="需集团协调支持的事项"
|
|
|
|
|
- prop="support"
|
|
|
|
|
- class="meeting-form-item mb-0! min-w-0"
|
|
|
|
|
- >
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="operationMeeting.support"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入需集团协调支持的事项"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </section>
|
|
|
|
|
- </section>
|
|
|
|
|
|
|
+ <OperationMeetingContent
|
|
|
|
|
+ :meeting="operationMeeting"
|
|
|
|
|
+ :details="detailItems"
|
|
|
|
|
+ :type="type"
|
|
|
|
|
+ :dept-options="deptOptions"
|
|
|
|
|
+ :loading="loading"
|
|
|
|
|
+ @update:meeting="updateOperationMeeting"
|
|
|
|
|
+ @add-detail="handleAddDetailItem"
|
|
|
|
|
+ @edit-detail="handleEditDetailItem"
|
|
|
|
|
+ @delete-detail="handleDeleteDetailItem"
|
|
|
|
|
+ />
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
|
|
|
<template #footer>
|
|
<template #footer>
|
|
@@ -857,496 +346,13 @@ onMounted(() => {
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
- <el-drawer
|
|
|
|
|
- :model-value="detailDrawerVisible"
|
|
|
|
|
- @update:model-value="handleDetailDrawerChange"
|
|
|
|
|
- :append-to-body="true"
|
|
|
|
|
- :size="detailDrawerSize"
|
|
|
|
|
- :close-on-click-modal="false"
|
|
|
|
|
- :close-on-press-escape="false"
|
|
|
|
|
- :show-close="false"
|
|
|
|
|
- header-class="mb-0! p-4!"
|
|
|
|
|
- body-class="bg-gray-100"
|
|
|
|
|
- footer-class="p-4!"
|
|
|
|
|
- >
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <div class="flex items-center">
|
|
|
|
|
- <span class="font-bold text-xl">{{ detailDrawerTitle }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
-
|
|
|
|
|
- <el-form
|
|
|
|
|
- ref="detailFormRef"
|
|
|
|
|
- label-position="top"
|
|
|
|
|
- size="default"
|
|
|
|
|
- :model="detailForm"
|
|
|
|
|
- :rules="detailRules"
|
|
|
|
|
- :disabled="type === 'view'"
|
|
|
|
|
- scroll-to-error
|
|
|
|
|
- require-asterisk-position="right"
|
|
|
|
|
- >
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
- <div class="detail-section__grid detail-section__grid--single">
|
|
|
|
|
- <el-form-item label="项目名称" prop="projectName">
|
|
|
|
|
- <el-autocomplete
|
|
|
|
|
- v-model="detailForm.projectName"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- placeholder="请选择或输入项目名称"
|
|
|
|
|
- clearable
|
|
|
|
|
- :fetch-suggestions="queryProjectNameSearch"
|
|
|
|
|
- :trigger-on-focus="true"
|
|
|
|
|
- @change="handleProjectNameComplete"
|
|
|
|
|
- @select="handleProjectNameSelect"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
-
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
- <h4 class="detail-section__title">经营情况(万元)</h4>
|
|
|
|
|
- <div class="detail-section__grid">
|
|
|
|
|
- <el-form-item label="收入-本期" prop="currentRevenue">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.currentRevenue"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="收入-累计" prop="cumulativeRevenue">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.cumulativeRevenue"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="挂帐-本期" prop="currentOnAccount">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.currentOnAccount"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="挂帐-累计" prop="cumulativeOnAccount">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.cumulativeOnAccount"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="回款-本期" prop="currentPayment">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.currentPayment"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="回款-累计" prop="cumulativePayment">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.cumulativePayment"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
-
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
- <h4 class="detail-section__title">本期生产运行情况</h4>
|
|
|
|
|
- <div class="detail-section__grid">
|
|
|
|
|
- <el-form-item label="计划工作量" prop="plannedWorkload">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.plannedWorkload"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- placeholder="请输入计划工作量"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="实际完成" prop="actualCompletion">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.actualCompletion"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="3"
|
|
|
|
|
- placeholder="请输入实际完成"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="设备利用率(%)" prop="equipmentUtilizationRate">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="detailForm.equipmentUtilizationRate"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- :precision="2"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
-
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
- <h4 class="detail-section__title">生产管理情况及重点工作</h4>
|
|
|
|
|
- <div class="detail-section__grid">
|
|
|
|
|
- <el-form-item label="重点工作及完成情况" prop="keyWorkCompletion">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.keyWorkCompletion"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入重点工作及完成情况"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="存在问题及分析" prop="problemsAnalysis">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.problemsAnalysis"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入存在问题及分析"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
-
|
|
|
|
|
- <section class="detail-section">
|
|
|
|
|
- <h4 class="detail-section__title">下期工作计划</h4>
|
|
|
|
|
- <div class="detail-section__grid">
|
|
|
|
|
- <el-form-item label="计划工作量" prop="nextPlannedWorkload">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.nextPlannedWorkload"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入下期计划工作量"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="重点工作事项" prop="priorityTasks">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="detailForm.priorityTasks"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :rows="4"
|
|
|
|
|
- placeholder="请输入重点工作事项"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
- </el-form>
|
|
|
|
|
-
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button size="default" @click="handleDetailDrawerChange(false)">取消</el-button>
|
|
|
|
|
- <el-button size="default" v-if="type !== 'view'" type="primary" @click="saveDetailItem">
|
|
|
|
|
- 保存
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-drawer>
|
|
|
|
|
|
|
+ <MeetingDetailDrawer
|
|
|
|
|
+ :visible="detailDrawerVisible"
|
|
|
|
|
+ :detail="detailForm"
|
|
|
|
|
+ :type="type"
|
|
|
|
|
+ :form-type="detailFormType"
|
|
|
|
|
+ @update:visible="handleDetailDrawerChange"
|
|
|
|
|
+ @save="saveDetailItem"
|
|
|
|
|
+ />
|
|
|
</el-drawer>
|
|
</el-drawer>
|
|
|
</template>
|
|
</template>
|
|
|
-
|
|
|
|
|
-<style scoped lang="scss">
|
|
|
|
|
-.meeting-section__grid {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
- gap: 18px 20px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-section__grid-full {
|
|
|
|
|
- grid-column: 1 / -1;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: 112px minmax(0, 1fr);
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- align-items: stretch;
|
|
|
|
|
- padding: 6px 8px;
|
|
|
|
|
- background: linear-gradient(180deg, #f8fbff 0%, #fff 100%);
|
|
|
|
|
- border: 1px solid #dbeafe;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__title {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- padding-right: 8px;
|
|
|
|
|
- border-right: 1px solid #dbeafe;
|
|
|
|
|
-
|
|
|
|
|
- span {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- font-weight: 800;
|
|
|
|
|
- line-height: 20px;
|
|
|
|
|
- color: #1f2937;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- small {
|
|
|
|
|
- margin-top: 0;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 16px;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__grid {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- padding: 4px 6px;
|
|
|
|
|
- background: rgb(255 255 255 / 72%);
|
|
|
|
|
- border: 1px solid rgb(219 234 254 / 70%);
|
|
|
|
|
- border-radius: 7px;
|
|
|
|
|
-
|
|
|
|
|
- span {
|
|
|
|
|
- display: block;
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 16px;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- strong {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- align-items: baseline;
|
|
|
|
|
- gap: 3px;
|
|
|
|
|
- margin-top: 0;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- font-size: 15px;
|
|
|
|
|
- font-weight: 800;
|
|
|
|
|
- line-height: 18px;
|
|
|
|
|
- color: #1b71f6;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- em {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- font-style: normal;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #64748b;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__item--revenue strong {
|
|
|
|
|
- color: #1b71f6;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__item--account strong {
|
|
|
|
|
- color: #f59e0b;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-summary-strip__item--payment strong {
|
|
|
|
|
- color: #10b981;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-table {
|
|
|
|
|
- --zm-table-font-size: 18px;
|
|
|
|
|
- --zm-table-header-font-size: 18px;
|
|
|
|
|
- --zm-table-header-font-weight: 800;
|
|
|
|
|
- --zm-table-row-font-weight: 800;
|
|
|
|
|
- --zm-table-header-text-color: #333;
|
|
|
|
|
- --zm-table-border-color: #cbd5e1;
|
|
|
|
|
- --zm-table-header-border-color: #c2ccda;
|
|
|
|
|
- --zm-table-row-border-color: #d4dce8;
|
|
|
|
|
-
|
|
|
|
|
- :deep(.header-wrapper) {
|
|
|
|
|
- height: 20px;
|
|
|
|
|
-
|
|
|
|
|
- .truncate {
|
|
|
|
|
- height: 20px;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-support-panel {
|
|
|
|
|
- padding-top: 18px;
|
|
|
|
|
- margin-top: 18px;
|
|
|
|
|
- border-top: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-form-tip {
|
|
|
|
|
- margin-top: 6px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- line-height: 18px;
|
|
|
|
|
- color: #909399;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-section {
|
|
|
|
|
- padding: 20px;
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border: 1px solid rgb(229 231 235 / 90%);
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-section__title {
|
|
|
|
|
- margin: 0 0 16px;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
- color: #1f2937;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-section__grid {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
- gap: 18px 24px;
|
|
|
|
|
-
|
|
|
|
|
- :deep(.el-form-item) {
|
|
|
|
|
- margin-bottom: 0;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-section__grid--single {
|
|
|
|
|
- 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 {
|
|
|
|
|
- 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 {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
- gap: 10px 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-detail-card__field {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-detail-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;
|
|
|
|
|
- white-space: pre-wrap;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.meeting-detail-card__actions {
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
- padding-top: 2px;
|
|
|
|
|
- border-top: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-@media (width <= 960px) {
|
|
|
|
|
- .meeting-section__grid {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .meeting-summary-strip {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .meeting-summary-strip__title {
|
|
|
|
|
- padding-right: 0;
|
|
|
|
|
- padding-bottom: 12px;
|
|
|
|
|
- border-right: 0;
|
|
|
|
|
- border-bottom: 1px solid #dbeafe;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .meeting-summary-strip__grid {
|
|
|
|
|
- grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .detail-section__grid {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-@media (width <= 768px) {
|
|
|
|
|
- .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-summary-strip__grid,
|
|
|
|
|
- .meeting-detail-card__fields {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|