You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

807 lines
32 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<ContentWrap>
<div v-loading="detailLoading" class="mold-brand-detail">
<div class="mold-brand-detail__hero">
<div class="mold-brand-detail__left">
<div class="mold-brand-detail__image-card">
<el-image v-if="imageList.length" :src="imageList[0]" :preview-src-list="imageList" fit="cover"
preview-teleported class="mold-brand-detail__image" />
<el-empty v-else description="暂无图片" />
</div>
<div class="mold-brand-detail__qr-card">
<div class="mold-brand-detail__qr-title">模具二维码</div>
<QrcodeActionCard :image-url="detailData?.qrCodeUrl" :print-id="detailData?.id" :print-template-type="4"
:print-title="`${detailData?.name || '模具'}码打印预览`" :print-paper-width="80" :print-paper-height="80"
:print-max-width="220" empty-text="暂无二维码" error-text="二维码加载失败" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!detailData?.id || !detailData?.code" refresh-confirm-text="确认刷新该模具二维码吗?"
@refresh-success="handleQrcodeRefreshSuccess" />
<div class="mold-brand-detail__qr-code">{{ detailData?.code || '-' }}</div>
</div>
</div>
<div class="mold-brand-detail__main">
<div class="mold-brand-detail__topbar">
<div>
<div class="mold-brand-detail__title-row">
<span class="mold-brand-detail__title">{{ detailData?.name || '-' }}<span v-if="detailData?.version">{{
detailData.version }}</span></span>
<dict-tag :type="DICT_TYPE.ERP_MOLD_STATUS" :value="detailData?.status" />
</div>
</div>
<div class="mold-brand-detail__actions">
<el-button @click="goBack"><Icon icon="ep:arrow-left" class="mr-5px" /> 返回</el-button>
<el-button type="primary" @click="handleReservedAction('上模')">上模</el-button>
<el-button type="success" @click="handleReservedAction('下模')">下模</el-button>
<el-button type="warning" @click="handleReservedAction('维修')">维修</el-button>
</div>
</div>
<div class="mold-brand-detail__grid">
<div class="mold-brand-detail__field"><span>编码</span><strong>{{ detailData?.code || '-' }}</strong></div>
<div class="mold-brand-detail__field"><span>当前设备</span><strong>{{ detailData?.currentDevice ||
detailData?.machineName
|| '-' }}</strong></div>
<div class="mold-brand-detail__field"><span>产品型号</span><strong>{{ detailData?.productName || '-' }}</strong>
</div>
<div class="mold-brand-detail__field"><span>版本</span><strong>{{ detailData?.version || '-' }}</strong></div>
<div class="mold-brand-detail__field mold-brand-detail__field--life">
<span>寿命状态</span>
<div v-if="lifeRate !== null" class="mold-brand-detail__life">
<el-progress :percentage="lifeRate" :stroke-width="10" :show-text="true" />
</div>
<strong v-else>-</strong>
</div>
<div class="mold-brand-detail__field"><span>子模数量</span><strong>{{ childMolds.length || detailData?.moldSize
|| 0
}}</strong></div>
</div>
<div class="mold-brand-detail__tabs">
<el-tabs v-model="activeTab">
<el-tab-pane label="子模具" name="molds">
<component :is="MoldListComp" :brand-id="brandId" />
</el-tab-pane>
<el-tab-pane label="点检记录" name="inspection">
<el-form :inline="true" class="device-ledger-tab-toolbar">
<el-form-item label="时间">
<el-date-picker v-model="inspectionDateRange" type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" :unlink-panels="true"
style="width: 340px" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="handleQueryInspection">查询</el-button>
<el-button @click="handleResetInspection">重置</el-button>
<el-button type="success" plain :loading="inspectionExportLoading"
@click="handleExportInspection">导出</el-button>
</el-form-item>
</el-form>
<el-empty v-if="!inspectionStepGroups.length" />
<el-steps v-else direction="vertical" :active="inspectionStepGroups.length"
class="device-ledger-history-steps">
<el-step v-for="group in inspectionStepGroups" :key="group.key">
<template #title>
<div class="device-ledger-history-title">
<span class="device-ledger-history-time">[{{ group.time }}]</span>
<span class="device-ledger-history-operator">操作人: {{ group.operator }}</span>
</div>
</template>
<template #description>
<div class="device-ledger-history-items">
<div v-for="item in group.items" :key="item.key" class="device-ledger-history-item">
<div class="device-ledger-history-item-head">
<el-tag :type="getResultTagType(item.result)">{{ getResultLabel(item.result) }}</el-tag>
<span class="device-ledger-history-item-text">{{ item.name }}</span>
</div>
<div class="device-ledger-history-item-body">
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">点检方式</span><span
class="device-ledger-history-item-value"><dict-tag type="Inspection_method"
:value="item.method" /></span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">判定标准</span><span
class="device-ledger-history-item-value">{{ item.criteria ?? '-' }}</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">点检时间</span><span
class="device-ledger-history-item-value">{{ formatHistoryTime(item.taskTime) }}</span>
</div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">备注</span><span
class="device-ledger-history-item-value">{{ item.remark ?? '-' }}</span></div>
</div>
</div>
</div>
</template>
</el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="维修记录" name="repair">
<el-form :inline="true" class="device-ledger-tab-toolbar">
<el-form-item label="时间">
<el-date-picker v-model="repairDateRange" type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" :unlink-panels="true"
style="width: 340px" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="handleQueryRepair">查询</el-button>
<el-button @click="handleResetRepair">重置</el-button>
<el-button type="success" plain :loading="repairExportLoading"
@click="handleExportRepair">导出</el-button>
</el-form-item>
</el-form>
<el-empty v-if="!repairGroups.length" />
<el-collapse v-else v-model="repairActiveNames" class="device-ledger-repair-collapse">
<el-collapse-item v-for="group in repairGroups" :key="group.key" :name="group.key">
<template #title>
<div class="device-ledger-repair-title">
<span class="device-ledger-repair-name">{{ group.name }}</span>
<span class="device-ledger-repair-meta">共{{ group.items.length }}条</span>
</div>
</template>
<div class="device-ledger-history-items">
<div v-for="row in group.items" :key="String(row.id ?? row.subjectId ?? row.subjectCode)"
class="device-ledger-history-item">
<div class="device-ledger-history-item-head">
<el-tag type="info" effect="light">{{ row.subjectCode ?? '-' }}</el-tag>
<span class="device-ledger-history-item-text">{{ row.subjectName ?? '-' }}</span>
</div>
<div class="device-ledger-history-item-body">
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">项目内容</span><span
class="device-ledger-history-item-value">{{ row.subjectContent ?? '-' }}</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">维修结果</span><span
class="device-ledger-history-item-value"><el-tag
:type="getResultTagType(row.result ?? row.repairResult)">{{
getResultLabel(row.repairResult ??
row.result) }}</el-tag></span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">完成日期</span><span
class="device-ledger-history-item-value">{{
String(formatHistoryTime(row.finishDate)).split(' ')[0]
}}</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">备注</span><span
class="device-ledger-history-item-value">{{
row.remark ?? '-' }}</span></div>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
<el-tab-pane label="保养记录" name="maintain">
<el-form :inline="true" class="device-ledger-tab-toolbar">
<el-form-item label="时间">
<el-date-picker v-model="maintainDateRange" type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" :unlink-panels="true"
style="width: 340px" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="handleQueryMaintain">查询</el-button>
<el-button @click="handleResetMaintain">重置</el-button>
<el-button type="success" plain :loading="maintainExportLoading"
@click="handleExportMaintain">导出</el-button>
</el-form-item>
</el-form>
<el-empty v-if="!maintainStepGroups.length" />
<el-steps v-else direction="vertical" :active="maintainStepGroups.length"
class="device-ledger-history-steps">
<el-step v-for="group in maintainStepGroups" :key="group.key">
<template #title>
<div class="device-ledger-history-title">
<span class="device-ledger-history-time">[{{ group.time }}]</span>
<span class="device-ledger-history-operator">操作人: {{ group.operator }}</span>
</div>
</template>
<template #description>
<div class="device-ledger-history-items">
<div v-for="item in group.items" :key="item.key" class="device-ledger-history-item">
<div class="device-ledger-history-item-head">
<el-tag :type="getResultTagType(item.result)">{{ getResultLabel(item.result) }}</el-tag>
<span class="device-ledger-history-item-text">{{ item.name }}</span>
</div>
<div class="device-ledger-history-item-body">
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">保养方式</span><span
class="device-ledger-history-item-value"><dict-tag type="Inspection_method"
:value="item.method" /></span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">判定标准</span><span
class="device-ledger-history-item-value">{{ item.criteria ?? '-' }}</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">保养时间</span><span
class="device-ledger-history-item-value">{{
String(formatHistoryTime(item.taskTime)).split(' ')[0] }}</span></div>
<div class="device-ledger-history-item-row"><span
class="device-ledger-history-item-label">备注</span><span
class="device-ledger-history-item-value">{{ item.remark ?? '-' }}</span></div>
</div>
</div>
</div>
</template>
</el-step>
</el-steps>
</el-tab-pane>
<el-tab-pane label="安装记录" name="install">
<el-form :inline="true" class="device-ledger-tab-toolbar">
<el-form-item label="模具"><el-select v-model="installMoldId" filterable clearable placeholder="选择模具"
class="!w-200px"><el-option v-for="item in childMolds" :key="item.id" :label="item.name"
:value="item.id" /></el-select></el-form-item>
<el-form-item label="备注"><el-input v-model="installRemark" placeholder="备注" clearable
@keyup.enter="handleQueryInstall" /></el-form-item>
<el-form-item label="时间">
<el-date-picker v-model="installDateRange" type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间" end-placeholder="结束时间" range-separator="至" :unlink-panels="true" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain @click="handleQueryInstall">查询</el-button>
<el-button @click="handleResetInstall">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="installRecords" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="模具名称" prop="moldName" min-width="150" sortable />
<el-table-column label="设备" prop="deviceName" min-width="120" sortable />
<el-table-column label="操作人" prop="creatorName" min-width="100" sortable />
<el-table-column label="备注" prop="remark" min-width="140" />
<el-table-column label="操作时间" prop="createTime" min-width="180" sortable>
<template #default="scope">{{ formatHistoryTime(scope.row.createTime) }}</template>
</el-table-column>
</el-table>
<el-empty v-if="!installRecords.length" description="暂无安装记录" />
<Pagination :total="installTotal" v-model:page="installPageNo" v-model:limit="installPageSize"
@pagination="fetchInstallRecords" />
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import download from '@/utils/download'
import { MoldBrandApi, type MoldBrandVO } from '@/api/erp/mold'
import { TicketManagementApi } from '@/api/mold/ticketManagement'
import { MoldRepairApi } from '@/api/mold/moldrepair'
import { MoldOperateApi } from '@/api/mes/moldoperate'
import QrcodeActionCard from '@/components/QrcodeActionCard/index.vue'
defineOptions({ name: 'ErpMoldBrandDetail' })
const message = useMessage()
const route = useRoute()
const router = useRouter()
const MoldListComp = defineAsyncComponent(() => import('../components/MoldList.vue') as any)
const brandId = computed(() => Number(route.params.id))
const detailLoading = ref(false)
const detailData = ref<(MoldBrandVO & Record<string, any>) | null>(null)
const childMolds = ref<any[]>([])
const activeTab = ref('molds')
const inspectionHistory = ref<any[]>([])
const maintainHistory = ref<any[]>([])
const repairList = ref<any[]>([])
const installRecords = ref<any[]>([])
const installTotal = ref(0)
const installPageNo = ref(1)
const installPageSize = ref(10)
const installMoldId = ref<number | undefined>()
const installRemark = ref<string | undefined>()
const installDateRange = ref<string[] | undefined>()
const repairActiveNames = ref<string[]>([])
const inspectionDateRange = ref<string[] | undefined>()
const maintainDateRange = ref<string[] | undefined>()
const repairDateRange = ref<string[] | undefined>()
const inspectionExportLoading = ref(false)
const maintainExportLoading = ref(false)
const repairExportLoading = ref(false)
const imageList = computed(() => parseImages(detailData.value?.images))
const lifeRate = computed<number | null>(() => {
const raw = detailData.value?.lifeRate ?? detailData.value?.lifeStatus ?? detailData.value?.useRate
if (raw === undefined || raw === null || raw === '') return null
const value = Number(raw)
return Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : null
})
const formatSimpleDate = (value: any) => {
if (!value) return '-'
const date = new Date(value)
return Number.isNaN(date.getTime()) ? String(value) : formatDate(date, 'YYYY-MM-DD')
}
const formatHistoryTime = (value: any) => {
const raw = value ?? '-'
if (Array.isArray(raw) && raw.length >= 3) {
const [y, m, d, hh, mm, ss] = raw
const pad = (n: any) => String(n).padStart(2, '0')
if (hh !== undefined) return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:${pad(mm)}:${pad(ss)}`
return `${y}-${pad(m)}-${pad(d)}`
}
const asDate = new Date(raw)
if (!Number.isNaN(asDate.getTime())) return formatDate(asDate, 'YYYY-MM-DD HH:mm:ss')
return String(raw)
}
const parseImages = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.map(String).filter(Boolean)
return String(value)
.replace(/[`'\"]/g, '')
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
const getResultLabel = (value: any) => {
const v = value === '' || value === null || value === undefined ? undefined : String(value)
if (!v) return '-'
const upper = v.toUpperCase()
if (v === '0') return '待检测'
if (v === '1' || upper === 'OK') return '通过'
if (v === '2' || upper === 'NG') return '不通过'
return v
}
const getResultTagType = (value: any) => {
const v = value === '' || value === null || value === undefined ? undefined : String(value)
if (!v) return 'info'
const upper = v.toUpperCase()
if (v === '1' || upper === 'OK') return 'success'
if (v === '2' || upper === 'NG') return 'danger'
return 'info'
}
type HistoryStepItem = {
key: string
name: string
result: any
method?: any
criteria?: any
remark?: any
taskTime?: any
}
type HistoryStepGroup = { key: string; time: string; operator: string; items: HistoryStepItem[] }
const buildStepGroups = (
rows: any[],
options: { timeField: string; nameFieldCandidates: string[]; resultFieldCandidates: string[] }
) => {
const groups = new Map<string, HistoryStepGroup>()
for (const row of rows ?? []) {
const time = formatHistoryTime(row?.taskTime ?? row?.[options.timeField] ?? row?.createTime)
const operator = String(row?.operator ?? row?.creatorName ?? row?.creator ?? '-')
const groupKey = `${row?.managementId ?? ''}__${time}__${operator}`
const name =
options.nameFieldCandidates
.map((key) => row?.[key])
.find((item) => item !== undefined && item !== null && String(item).trim() !== '') ?? '-'
const result =
options.resultFieldCandidates.map((key) => row?.[key]).find((item) => item !== undefined && item !== null) ?? undefined
const item: HistoryStepItem = {
key: String(row?.id ?? `${groupKey}_${name}`),
name: String(name),
result,
method: row?.inspectionMethod,
criteria: row?.judgmentCriteria,
remark: row?.remark,
taskTime: row?.taskTime
}
if (!groups.has(groupKey)) {
groups.set(groupKey, { key: groupKey, time, operator, items: [item] })
} else {
groups.get(groupKey)!.items.push(item)
}
}
return Array.from(groups.values()).sort((a, b) => String(b.time).localeCompare(String(a.time)))
}
const inspectionStepGroups = computed(() =>
buildStepGroups(inspectionHistory.value, {
timeField: 'inspectionTime',
nameFieldCandidates: ['inspectionItemName', 'name'],
resultFieldCandidates: ['inspectionResult']
})
)
const maintainStepGroups = computed(() =>
buildStepGroups(maintainHistory.value, {
timeField: 'inspectionTime',
nameFieldCandidates: ['maintainItemName', 'inspectionItemName', 'name'],
resultFieldCandidates: ['maintainResult', 'inspectionResult']
})
)
const repairGroups = computed(() => {
const groupsMap = new Map<string, { key: string; name: string; items: any[] }>()
for (const row of repairList.value ?? []) {
const key = String(row.repairCode ?? row.repairId ?? row.subjectName ?? '-')
if (!groupsMap.has(key)) {
groupsMap.set(key, { key, name: String(row.repairName ?? row.repairCode ?? key), items: [] })
}
groupsMap.get(key)!.items.push(row)
}
return Array.from(groupsMap.values())
})
const getQrcodeRefreshUrl = () => {
if (!detailData.value?.id || !detailData.value?.code) return ''
return `/erp/mold-brand/regenerate-code?id=${detailData.value.id}&code=${encodeURIComponent(String(detailData.value.code))}`
}
const handleQrcodeRefreshSuccess = async (data: any) => {
if (!detailData.value?.id) return
if (data?.qrcodeUrl) {
detailData.value.qrcodeUrl = data.qrcodeUrl
return
}
const moldData = await MoldBrandApi.getMoldBrand(detailData.value.id)
detailData.value.qrcodeUrl = moldData?.qrcodeUrl
detailData.value.code = moldData?.code ?? detailData.value.code
}
const goBack = () => {
router.push('/mold/mold-brand')
}
const handleReservedAction = (action: string) => {
message.info(`${action}功能预留中`)
}
let getChildMoldsPromise: Promise<any[]> | null = null
const getChildMolds = async () => {
if (!brandId.value) return []
if (getChildMoldsPromise) return getChildMoldsPromise
getChildMoldsPromise = (async () => {
const pageSize = 10
const allList: any[] = []
let pageNo = 1
let total = 0
while (true) {
const data = await MoldBrandApi.getMoldPage({ pageNo, pageSize, brandId: brandId.value })
const list = data?.list ?? []
allList.push(...list)
total = Number(data?.total ?? allList.length)
if (allList.length >= total || list.length < pageSize) break
pageNo++
}
childMolds.value = allList
return childMolds.value
})()
try {
return await getChildMoldsPromise
} finally {
getChildMoldsPromise = null
}
}
const collectByChildMolds = async (worker: (moldId: number) => Promise<any>, mergeList = true) => {
const molds = childMolds.value.length ? childMolds.value : await getChildMolds()
const ids = molds.map((item) => Number(item.id)).filter(Boolean)
if (!ids.length) return []
const results = await Promise.all(ids.map((id) => worker(id).catch(() => [])))
return mergeList ? results.flat() : results
}
const fetchInspectionHistory = async () => {
if (!brandId.value) return
const params: any = { moldId: brandId.value }
if (inspectionDateRange.value && inspectionDateRange.value.length === 2) {
params.startTime = inspectionDateRange.value[0]
params.endTime = inspectionDateRange.value[1]
}
const data = await TicketManagementApi.getInspectionByMoldId(params)
inspectionHistory.value = Array.isArray(data) ? data : []
}
const fetchMaintainHistory = async () => {
if (!brandId.value) return
const params: any = { moldId: brandId.value }
if (maintainDateRange.value && maintainDateRange.value.length === 2) {
params.startTime = maintainDateRange.value[0]
params.endTime = maintainDateRange.value[1]
}
const data = await TicketManagementApi.getMaintenanceByMoldId(params)
maintainHistory.value = Array.isArray(data) ? data : []
}
const fetchRepairHistory = async () => {
if (!brandId.value) return
const params: any = { moldId: brandId.value }
if (repairDateRange.value && repairDateRange.value.length === 2) {
params.startTime = repairDateRange.value[0]
params.endTime = repairDateRange.value[1]
}
const data = await MoldRepairApi.getRepairListByMoldId(params)
repairList.value = Array.isArray(data) ? data : []
repairActiveNames.value = repairGroups.value.map((item) => item.key)
}
const fetchInstallRecords = async () => {
if (!brandId.value) return
const params: any = { pageNo: installPageNo.value, pageSize: installPageSize.value, brandId: brandId.value }
if (installMoldId.value) params.moldId = installMoldId.value
if (installRemark.value) params.remark = installRemark.value
if (installDateRange.value && installDateRange.value.length === 2) {
params.createTime = [installDateRange.value[0], installDateRange.value[1]]
}
const data = await MoldOperateApi.getMoldOperatePage(params)
installRecords.value = data?.list ?? []
installTotal.value = data?.total ?? 0
}
const handleQueryInstall = () => {
installPageNo.value = 1
fetchInstallRecords()
}
const handleResetInstall = () => {
installMoldId.value = undefined
installRemark.value = undefined
installDateRange.value = undefined
installPageNo.value = 1
fetchInstallRecords()
}
const handleQueryInspection = async () => {
await fetchInspectionHistory()
}
const handleResetInspection = async () => {
inspectionDateRange.value = undefined
await fetchInspectionHistory()
}
const handleExportInspection = async () => {
inspectionExportLoading.value = true
message.info('点检记录导出功能待后端确认后接入')
inspectionExportLoading.value = false
}
const handleQueryMaintain = async () => {
await fetchMaintainHistory()
}
const handleResetMaintain = async () => {
maintainDateRange.value = undefined
await fetchMaintainHistory()
}
const handleExportMaintain = async () => {
maintainExportLoading.value = true
message.info('保养记录导出功能待后端确认后接入')
maintainExportLoading.value = false
}
const handleQueryRepair = async () => {
await fetchRepairHistory()
}
const handleResetRepair = async () => {
repairDateRange.value = undefined
await fetchRepairHistory()
}
const handleExportRepair = async () => {
repairExportLoading.value = true
message.info('维修记录导出功能待后端确认后接入')
repairExportLoading.value = false
}
const getDetail = async () => {
if (!brandId.value) {
message.error('缺少模具组ID')
return
}
detailLoading.value = true
try {
detailData.value = await MoldBrandApi.getMoldBrand(brandId.value)
await getChildMolds()
await Promise.all([fetchInspectionHistory(), fetchMaintainHistory(), fetchRepairHistory(), fetchInstallRecords()])
} finally {
detailLoading.value = false
}
}
onMounted(() => {
getDetail()
})
</script>
<style scoped>
.mold-brand-detail__crumb {
margin-bottom: 16px;
color: var(--el-text-color-secondary);
font-size: 13px;
}
.mold-brand-detail__hero {
display: grid;
grid-template-columns: 320px minmax(0, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.mold-brand-detail__left {
display: flex;
flex-direction: column;
gap: 16px;
}
.mold-brand-detail__image-card,
.mold-brand-detail__qr-card {
padding: 16px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 16px;
background: #fff;
}
.mold-brand-detail__image {
width: 100%;
height: 280px;
border-radius: 12px;
}
.mold-brand-detail__qr-title {
margin-bottom: 12px;
color: var(--el-text-color-primary);
font-weight: 600;
text-align: center;
}
.mold-brand-detail__qr {
width: 180px;
height: 180px;
margin: 0 auto;
display: block;
}
.mold-brand-detail__qr-code {
margin-top: 12px;
text-align: center;
font-weight: 600;
}
.mold-brand-detail__main {
padding: 16px 20px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 16px;
background: #fff;
}
.mold-brand-detail__topbar {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 18px;
}
.mold-brand-detail__title-row {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.mold-brand-detail__title {
color: var(--el-text-color-primary);
font-size: 28px;
font-weight: 700;
}
.mold-brand-detail__actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.mold-brand-detail__grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18px 24px;
}
.mold-brand-detail__field {
display: flex;
flex-direction: column;
gap: 6px;
}
.mold-brand-detail__field span {
color: var(--el-text-color-secondary);
font-size: 13px;
}
.mold-brand-detail__field strong {
color: var(--el-text-color-primary);
font-size: 18px;
font-weight: 600;
}
.mold-brand-detail__field--life strong {
font-size: 14px;
}
.mold-brand-detail__tabs {
margin-top: 30px;
padding: 8px 12px 0;
border: 1px solid var(--el-border-color-lighter);
border-radius: 16px;
background: #fff;
}
.device-ledger-tab-toolbar {
margin-bottom: 16px;
}
.device-ledger-history-steps,
.device-ledger-repair-collapse {
margin-top: 10px;
}
.device-ledger-history-title,
.device-ledger-repair-title {
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
}
.device-ledger-history-time,
.device-ledger-repair-name {
font-weight: 600;
}
.device-ledger-history-items {
display: flex;
flex-direction: column;
gap: 12px;
}
.device-ledger-history-item {
padding: 12px 14px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 12px;
}
.device-ledger-history-item-head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.device-ledger-history-item-body {
display: grid;
gap: 8px;
}
.device-ledger-history-item-row {
display: grid;
grid-template-columns: 88px minmax(0, 1fr);
gap: 12px;
}
.device-ledger-history-item-label {
color: var(--el-text-color-secondary);
}
@media (max-width: 1024px) {
.mold-brand-detail__hero {
grid-template-columns: 1fr;
}
.mold-brand-detail__grid {
grid-template-columns: 1fr;
}
}
</style>