feat(pl): add plan contract completion page with inline expand
List only plans missing contracts and allow per-side multi-select save in an expand row, reusing PlPlanContractMultiSelect without contract CRUD. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
62bdf48b63
commit
368cb5567a
1 file changed
+703
@@ -0,0 +1,703 @@
|
||||
<!-- 运输计划合同完善:仅缺合同计划,行内展开分侧保存 -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<section class="panel">
|
||||
<div class="panel-top pl-plan-contract-panel-top">
|
||||
<h2 class="title-bar-heading">运输计划合同完善</h2>
|
||||
<span class="pl-plan-contract-stat">待完善 {{ pagination.total }} 条</span>
|
||||
</div>
|
||||
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="计划编号">
|
||||
<el-input
|
||||
v-model="searchForm.planNo"
|
||||
placeholder="搜索计划编号"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="缺口类型">
|
||||
<el-select v-model="searchForm.gapType" placeholder="全部缺口" clearable>
|
||||
<el-option label="全部缺口" value="" />
|
||||
<el-option label="仅缺供方合同" value="supplier" />
|
||||
<el-option label="仅缺需方合同" value="demander" />
|
||||
<el-option label="两侧均缺" value="both" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="供方 / 需方">
|
||||
<el-input
|
||||
v-model="searchForm.customerKeyword"
|
||||
placeholder="客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<div class="filter-actions-start" />
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="list-toolbar">
|
||||
<div class="list-toolbar-row pl-plan-list-toolbar-row">
|
||||
<ListToolbarDateShortcuts
|
||||
variant="pills"
|
||||
:active="activeDateShortcut"
|
||||
aria-label="计划日期快捷筛选"
|
||||
@change="handleDateShortcut"
|
||||
/>
|
||||
<ListQuickSearchInput
|
||||
v-model="quickSearch"
|
||||
placeholder="计划编号/供方/需方"
|
||||
@search="handleQuickSearch"
|
||||
@clear="handleQuickSearchClear"
|
||||
/>
|
||||
<el-button class="btn-ghost" :loading="loading" @click="fetchData">
|
||||
<el-icon><Refresh /></el-icon>刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
class="list-table"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
:expand-row-keys="expandedRowKeys"
|
||||
@expand-change="handleExpandChange"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="expand" min-width="48">
|
||||
<template #default="{ row }">
|
||||
<div class="pl-contract-expand" v-if="expandDraft[row.id]">
|
||||
<div class="pl-contract-expand__grid">
|
||||
<div
|
||||
class="pl-contract-side"
|
||||
:class="sideBoxClass(row, 'supplier')"
|
||||
>
|
||||
<div class="pl-contract-side__head">
|
||||
<h4>供方合同</h4>
|
||||
<el-tag
|
||||
:type="sideTagType(row, 'supplier')"
|
||||
size="small"
|
||||
:effect="row.supplierContractExempt ? 'plain' : 'dark'"
|
||||
>
|
||||
{{ sideTagLabel(row, 'supplier') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<p class="pl-contract-side__customer">
|
||||
{{ row.supplierName || '—' }}
|
||||
</p>
|
||||
<template v-if="row.supplierContractExempt">
|
||||
<p class="pl-contract-side__exempt">内置公司,合同可免</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<PlPlanContractMultiSelect
|
||||
v-model="expandDraft[row.id].supplierContracts"
|
||||
:customer-id="row.supplierId"
|
||||
:disabled="!row.supplierId"
|
||||
:required="false"
|
||||
/>
|
||||
<div class="pl-contract-side__actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="savingKey === `${row.id}:supplier`"
|
||||
@click="handleSaveSide(row, 'supplier')"
|
||||
>
|
||||
<el-icon><Check /></el-icon>保存供方合同
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="pl-contract-side"
|
||||
:class="sideBoxClass(row, 'demander')"
|
||||
>
|
||||
<div class="pl-contract-side__head">
|
||||
<h4>需方合同</h4>
|
||||
<el-tag
|
||||
:type="sideTagType(row, 'demander')"
|
||||
size="small"
|
||||
:effect="row.demanderContractExempt ? 'plain' : 'dark'"
|
||||
>
|
||||
{{ sideTagLabel(row, 'demander') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<p class="pl-contract-side__customer">
|
||||
{{ row.customerName || '—' }}
|
||||
</p>
|
||||
<template v-if="row.demanderContractExempt">
|
||||
<p class="pl-contract-side__exempt">内置公司,合同可免</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<PlPlanContractMultiSelect
|
||||
v-model="expandDraft[row.id].demanderContracts"
|
||||
:customer-id="row.customerId"
|
||||
:disabled="!row.customerId"
|
||||
:required="false"
|
||||
/>
|
||||
<div class="pl-contract-side__actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="savingKey === `${row.id}:demander`"
|
||||
@click="handleSaveSide(row, 'demander')"
|
||||
>
|
||||
<el-icon><Check /></el-icon>保存需方合同
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
:index="index => (pagination.page - 1) * pagination.pageSize + index + 1"
|
||||
/>
|
||||
<el-table-column prop="planNo" label="计划编号" min-width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.planNo }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="planDate" label="计划日期" min-width="120" align="center" />
|
||||
<el-table-column
|
||||
prop="supplierName"
|
||||
label="供方"
|
||||
min-width="200"
|
||||
align="left"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="party-cell">
|
||||
<span>{{ row.supplierName || '—' }}</span>
|
||||
<span class="party-cell__sub">{{ partySubText(row, 'supplier') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="customerName"
|
||||
label="需方"
|
||||
min-width="200"
|
||||
align="left"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="party-cell">
|
||||
<span>{{ row.customerName || '—' }}</span>
|
||||
<span class="party-cell__sub">{{ partySubText(row, 'demander') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合同缺口" min-width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="gapTag(row).type" size="small" effect="dark">
|
||||
{{ gapTag(row).text }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="followStatus" label="跟进状态" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="getPlPlanFollowStatusTagType(row.followStatus)"
|
||||
size="small"
|
||||
effect="dark"
|
||||
>
|
||||
{{ getPlPlanFollowStatusLabel(row.followStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" min-width="200" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
@click="toggleExpand(row)"
|
||||
>
|
||||
<el-icon><EditPen /></el-icon>
|
||||
{{ isExpanded(row.id) ? '收起' : '展开补充' }}
|
||||
</el-button>
|
||||
<el-button link type="primary" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看计划
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination list-pager-bar">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
background
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<PlPlanViewDialog
|
||||
ref="viewDialogRef"
|
||||
v-model:visible="viewVisible"
|
||||
:plan-id="viewPlanId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Check, EditPen, Refresh, RefreshLeft, Search, View } from '@element-plus/icons-vue'
|
||||
import {
|
||||
planApi,
|
||||
type PlanContractGapItem,
|
||||
type PlanContractLink
|
||||
} from '@/api/pl'
|
||||
import PlPlanContractMultiSelect from '@/components/business/pl-plan-contract-multi-select/index.vue'
|
||||
import PlPlanViewDialog from '@/components/business/pl-plan-view-dialog/index.vue'
|
||||
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
|
||||
import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortcuts/index.vue'
|
||||
import { useListDateShortcut } from '@/composables/useListDateShortcut'
|
||||
import {
|
||||
getPlPlanFollowStatusLabel,
|
||||
getPlPlanFollowStatusTagType
|
||||
} from '@/utils/pl-display'
|
||||
import { refreshMenuBadges } from '@/utils/menu-badge-registry'
|
||||
import { useMenuStore } from '@/store/modules/menu'
|
||||
|
||||
const menuStore = useMenuStore()
|
||||
const loading = ref(false)
|
||||
const tableRef = ref()
|
||||
const tableData = ref<PlanContractGapItem[]>([])
|
||||
const dateRange = ref<string[]>([])
|
||||
const quickSearch = ref('')
|
||||
const expandedRowKeys = ref<string[]>([])
|
||||
const expandDraft = reactive<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
supplierContracts: PlanContractLink[]
|
||||
demanderContracts: PlanContractLink[]
|
||||
baselineSupplier: string
|
||||
baselineDemander: string
|
||||
}
|
||||
>
|
||||
>({})
|
||||
const savingKey = ref('')
|
||||
const viewDialogRef = ref<InstanceType<typeof PlPlanViewDialog>>()
|
||||
const viewVisible = ref(false)
|
||||
const viewPlanId = ref('')
|
||||
|
||||
const searchForm = reactive({
|
||||
planNo: '',
|
||||
gapType: '' as '' | 'supplier' | 'demander' | 'both',
|
||||
customerKeyword: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
function cloneLinks(links?: PlanContractLink[]): PlanContractLink[] {
|
||||
return (links || []).map(item => ({
|
||||
contractId: item.contractId,
|
||||
contractNo: item.contractNo
|
||||
}))
|
||||
}
|
||||
|
||||
function linksFingerprint(links: PlanContractLink[]) {
|
||||
return cloneLinks(links)
|
||||
.map(item => item.contractId)
|
||||
.sort()
|
||||
.join(',')
|
||||
}
|
||||
|
||||
function gapTag(row: PlanContractGapItem) {
|
||||
const sides = row.gapSides || []
|
||||
if (sides.includes('supplier') && sides.includes('demander')) {
|
||||
return { text: '两侧均缺', type: 'danger' as const }
|
||||
}
|
||||
if (sides.includes('supplier')) {
|
||||
return { text: '缺供方合同', type: 'warning' as const }
|
||||
}
|
||||
if (sides.includes('demander')) {
|
||||
return { text: '缺需方合同', type: 'warning' as const }
|
||||
}
|
||||
return { text: '—', type: 'info' as const }
|
||||
}
|
||||
|
||||
function partySubText(row: PlanContractGapItem, side: 'supplier' | 'demander') {
|
||||
if (side === 'supplier') {
|
||||
if (row.supplierContractExempt) return '内置 · 合同可免'
|
||||
const n = row.supplierContracts?.length || 0
|
||||
return n ? `已关联 ${n} 份` : '未关联'
|
||||
}
|
||||
if (row.demanderContractExempt) return '内置 · 合同可免'
|
||||
const n = row.demanderContracts?.length || 0
|
||||
return n ? `已关联 ${n} 份` : '未关联'
|
||||
}
|
||||
|
||||
function sideBoxClass(row: PlanContractGapItem, side: 'supplier' | 'demander') {
|
||||
const exempt = side === 'supplier' ? row.supplierContractExempt : row.demanderContractExempt
|
||||
if (exempt) return 'is-exempt'
|
||||
const missing = (row.gapSides || []).includes(side)
|
||||
return missing ? 'is-need' : 'is-ok'
|
||||
}
|
||||
|
||||
function sideTagLabel(row: PlanContractGapItem, side: 'supplier' | 'demander') {
|
||||
const exempt = side === 'supplier' ? row.supplierContractExempt : row.demanderContractExempt
|
||||
if (exempt) return '可免'
|
||||
return (row.gapSides || []).includes(side) ? '待补充' : '已齐'
|
||||
}
|
||||
|
||||
function sideTagType(row: PlanContractGapItem, side: 'supplier' | 'demander') {
|
||||
const exempt = side === 'supplier' ? row.supplierContractExempt : row.demanderContractExempt
|
||||
if (exempt) return 'info'
|
||||
return (row.gapSides || []).includes(side) ? 'warning' : 'success'
|
||||
}
|
||||
|
||||
function isExpanded(id: string) {
|
||||
return expandedRowKeys.value.includes(id)
|
||||
}
|
||||
|
||||
function ensureDraft(row: PlanContractGapItem) {
|
||||
if (expandDraft[row.id]) return
|
||||
const supplierContracts = cloneLinks(row.supplierContracts)
|
||||
const demanderContracts = cloneLinks(row.demanderContracts)
|
||||
expandDraft[row.id] = {
|
||||
supplierContracts,
|
||||
demanderContracts,
|
||||
baselineSupplier: linksFingerprint(supplierContracts),
|
||||
baselineDemander: linksFingerprint(demanderContracts)
|
||||
}
|
||||
}
|
||||
|
||||
function isDraftDirty(id: string) {
|
||||
const draft = expandDraft[id]
|
||||
if (!draft) return false
|
||||
return (
|
||||
linksFingerprint(draft.supplierContracts) !== draft.baselineSupplier ||
|
||||
linksFingerprint(draft.demanderContracts) !== draft.baselineDemander
|
||||
)
|
||||
}
|
||||
|
||||
async function collapseRow(id: string) {
|
||||
if (isDraftDirty(id)) {
|
||||
try {
|
||||
await ElMessageBox.confirm('当前有未保存的合同变更,确认收起吗?', '提示', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '收起',
|
||||
cancelButtonText: '继续编辑'
|
||||
})
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
delete expandDraft[id]
|
||||
expandedRowKeys.value = expandedRowKeys.value.filter(key => key !== id)
|
||||
return true
|
||||
}
|
||||
|
||||
async function expandRow(row: PlanContractGapItem) {
|
||||
const current = expandedRowKeys.value[0]
|
||||
if (current && current !== row.id) {
|
||||
const ok = await collapseRow(current)
|
||||
if (!ok) return
|
||||
}
|
||||
ensureDraft(row)
|
||||
expandedRowKeys.value = [row.id]
|
||||
}
|
||||
|
||||
async function toggleExpand(row: PlanContractGapItem) {
|
||||
if (isExpanded(row.id)) {
|
||||
await collapseRow(row.id)
|
||||
} else {
|
||||
await expandRow(row)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleExpandChange(row: PlanContractGapItem, expandedRows: PlanContractGapItem[]) {
|
||||
const willExpand = expandedRows.some(item => item.id === row.id)
|
||||
if (willExpand) {
|
||||
const current = expandedRowKeys.value[0]
|
||||
if (current && current !== row.id) {
|
||||
const ok = await collapseRow(current)
|
||||
if (!ok) {
|
||||
nextTick(() => {
|
||||
tableRef.value?.toggleRowExpansion?.(row, false)
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
ensureDraft(row)
|
||||
expandedRowKeys.value = [row.id]
|
||||
} else if (expandedRowKeys.value.includes(row.id)) {
|
||||
const ok = await collapseRow(row.id)
|
||||
if (!ok) {
|
||||
nextTick(() => {
|
||||
tableRef.value?.toggleRowExpansion?.(row, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const keyword = quickSearch.value.trim()
|
||||
const res = await planApi.listContractGaps({
|
||||
q: keyword || undefined,
|
||||
planNo: keyword ? undefined : searchForm.planNo || undefined,
|
||||
gapType: searchForm.gapType || undefined,
|
||||
customerKeyword: keyword ? undefined : searchForm.customerKeyword || undefined,
|
||||
planDateFrom: dateRange.value?.[0] || undefined,
|
||||
planDateTo: dateRange.value?.[1] || undefined,
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize
|
||||
})
|
||||
const data = (res as any).data ?? res
|
||||
tableData.value = data.list || []
|
||||
pagination.total = data.total || 0
|
||||
// 收起已不在列表中的展开行
|
||||
const ids = new Set(tableData.value.map(item => item.id))
|
||||
expandedRowKeys.value = expandedRowKeys.value.filter(id => ids.has(id))
|
||||
Object.keys(expandDraft).forEach(id => {
|
||||
if (!ids.has(id)) delete expandDraft[id]
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.planNo = ''
|
||||
searchForm.gapType = ''
|
||||
searchForm.customerKeyword = ''
|
||||
quickSearch.value = ''
|
||||
resetDateShortcut()
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleQuickSearch() {
|
||||
searchForm.planNo = ''
|
||||
searchForm.customerKeyword = ''
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleQuickSearchClear() {
|
||||
quickSearch.value = ''
|
||||
handleQuickSearch()
|
||||
}
|
||||
|
||||
const { activeDateShortcut, handleDateShortcut, resetDateShortcut } = useListDateShortcut(
|
||||
dateRange,
|
||||
handleSearch
|
||||
)
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleView(row: PlanContractGapItem) {
|
||||
viewPlanId.value = row.id
|
||||
viewVisible.value = true
|
||||
}
|
||||
|
||||
async function handleSaveSide(row: PlanContractGapItem, side: 'supplier' | 'demander') {
|
||||
const draft = expandDraft[row.id]
|
||||
if (!draft) return
|
||||
const contracts =
|
||||
side === 'supplier' ? draft.supplierContracts : draft.demanderContracts
|
||||
savingKey.value = `${row.id}:${side}`
|
||||
try {
|
||||
const res = await planApi.updateSideContracts(row.id, side, contracts)
|
||||
const updated = ((res as any).data ?? res) as PlanContractGapItem
|
||||
ElMessage.success(side === 'supplier' ? '供方合同已保存' : '需方合同已保存')
|
||||
|
||||
if (!(updated.gapSides || []).length) {
|
||||
tableData.value = tableData.value.filter(item => item.id !== row.id)
|
||||
pagination.total = Math.max(0, pagination.total - 1)
|
||||
delete expandDraft[row.id]
|
||||
expandedRowKeys.value = []
|
||||
await refreshMenuBadges(menuStore.menuList)
|
||||
return
|
||||
}
|
||||
|
||||
const idx = tableData.value.findIndex(item => item.id === row.id)
|
||||
if (idx >= 0) {
|
||||
tableData.value[idx] = updated
|
||||
}
|
||||
draft.supplierContracts = cloneLinks(updated.supplierContracts)
|
||||
draft.demanderContracts = cloneLinks(updated.demanderContracts)
|
||||
draft.baselineSupplier = linksFingerprint(draft.supplierContracts)
|
||||
draft.baselineDemander = linksFingerprint(draft.demanderContracts)
|
||||
await refreshMenuBadges(menuStore.menuList)
|
||||
} finally {
|
||||
savingKey.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pl-plan-contract-panel-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 16px 20px 12px;
|
||||
border-bottom: 1px solid #eef2f6;
|
||||
}
|
||||
|
||||
.pl-plan-contract-stat {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #d97706;
|
||||
background: #fffbeb;
|
||||
border: 1px solid rgba(217, 119, 6, 0.2);
|
||||
}
|
||||
|
||||
.party-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.party-cell__sub {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary, #64748b);
|
||||
}
|
||||
|
||||
.pl-contract-expand {
|
||||
padding: 12px 16px 16px;
|
||||
background: #f8fffb;
|
||||
border-top: 1px dashed #a7f3d0;
|
||||
}
|
||||
|
||||
.pl-contract-expand__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.pl-contract-expand__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-contract-side {
|
||||
background: #fff;
|
||||
border: 1px solid var(--el-border-color, #d9e8e2);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.pl-contract-side.is-need {
|
||||
border-color: rgba(217, 119, 6, 0.35);
|
||||
background: #fffdf8;
|
||||
}
|
||||
|
||||
.pl-contract-side.is-ok {
|
||||
border-color: #a7f3d0;
|
||||
background: #f6fffb;
|
||||
}
|
||||
|
||||
.pl-contract-side.is-exempt {
|
||||
border-color: #e2e8f0;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.pl-contract-side__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-contract-side__customer {
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pl-contract-side__exempt {
|
||||
margin: 0;
|
||||
padding: 14px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
color: var(--el-text-color-secondary, #64748b);
|
||||
font-size: 12px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.pl-contract-side__actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user