feat:模具组-添加图纸、操作手册、操作视频

main
黄伟杰 1 week ago
parent a02a7d77a1
commit 025b619a35

@ -11,6 +11,9 @@ export interface MoldBrandVO {
productIds?: Array<number> | string // 产品ID列表
products?: any[]
images?: string // 图片
drawings?: string // 图纸
operationManual?: string // 操作手册
operationVideo?: string // 操作视频
version?: string // 版本
status?: number // 状态
currentPosition?: string // 当前位置

@ -2713,6 +2713,9 @@ export default {
tabRepair: 'Repair Records',
tabMaintain: 'Maintenance Records',
tabInstall: 'Installation Records',
tabDrawings: 'Drawings',
tabOperationManual: 'Operation Manual',
tabOperationVideo: 'Operation Video',
time: 'Time',
startTime: 'Start Time',
endTime: 'End Time',
@ -2726,6 +2729,9 @@ export default {
images: 'Images',
remark: 'Remark',
operator: 'Operator',
drawings: 'Drawings',
operationManual: 'Operation Manual',
operationVideo: 'Operation Video',
projectContent: 'Project Content',
repairResult: 'Repair Result',
finishDate: 'Finish Date',
@ -2756,6 +2762,7 @@ export default {
creatorName: 'Operator',
createTime: 'Operation Time',
noInstallRecords: 'No installation records',
noRecords: 'No data',
pending: 'Pending',
pass: 'Pass',
fail: 'Fail',
@ -2775,6 +2782,9 @@ export default {
moldSize: 'Cavity Number',
useTime: 'Expected Life',
images: 'Image',
drawings: 'Drawings',
operationManual: 'Operation Manual',
operationVideo: 'Operation Video',
remark: 'Remark',
isEnable: 'Is Enable',
enable: 'Enable',

@ -2192,6 +2192,9 @@ export default {
tabRepair: '维修记录',
tabMaintain: '保养记录',
tabInstall: '安装记录',
tabDrawings: '图纸',
tabOperationManual: '操作手册',
tabOperationVideo: '操作视频',
// 筛选
time: '时间',
startTime: '开始时间',
@ -2207,6 +2210,9 @@ export default {
images: '图片',
remark: '备注',
operator: '操作人',
drawings: '图纸',
operationManual: '操作手册',
operationVideo: '操作视频',
// 维修
projectContent: '项目内容',
repairResult: '维修结果',
@ -2240,6 +2246,7 @@ export default {
creatorName: '操作人',
createTime: '操作时间',
noInstallRecords: '暂无安装记录',
noRecords: '暂无数据',
// 检测结果
pending: '待检测',
pass: '通过',
@ -2260,6 +2267,9 @@ export default {
moldSize: '模穴数',
useTime: '预期寿命',
images: '图片',
drawings: '图纸',
operationManual: '操作手册',
operationVideo: '操作视频',
inputValue: '输入值',
remark: '备注',
isEnable: '是否启用',

@ -61,6 +61,31 @@
<UploadImg v-model="formData.images" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('MoldManagement.MoldBrandFormPage.drawings')" prop="drawings">
<UploadImgs v-model="drawingsValue" :limit="9" width="96px" height="96px" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('MoldManagement.MoldBrandFormPage.operationManual')" prop="operationManual">
<UploadFile
v-model="operationManualValue"
:limit="9"
:file-type="manualFileTypes"
:is-show-tip="false"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('MoldManagement.MoldBrandFormPage.operationVideo')" prop="operationVideo">
<UploadFile
v-model="operationVideoValue"
:limit="9"
:file-type="videoFileTypes"
:is-show-tip="false"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="t('MoldManagement.MoldBrandFormPage.remark')" prop="remark">
<el-input v-model="formData.remark" type="textarea" :placeholder="t('MoldManagement.MoldBrandFormPage.placeholderRemark')" />
@ -213,6 +238,9 @@ const formData = ref<MoldBrandVO>({
productName: '',
productIds: [],
images: '',
drawings: '',
operationManual: '',
operationVideo: '',
status: 0,
useTime: 0,
maintainType: undefined,
@ -223,6 +251,47 @@ const formData = ref<MoldBrandVO>({
isCode: true
})
const manualFileTypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
const videoFileTypes = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm']
const splitAssetValue = (value: unknown): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.flatMap((item) => splitAssetValue(item))
if (typeof value === 'object' && (value as any)?.fileUrl) {
return [String((value as any).fileUrl).trim()].filter(Boolean)
}
const text = String(value).trim()
if (!text) return []
if (text.startsWith('{') || text.startsWith('[')) {
try {
const parsed = JSON.parse(text)
return splitAssetValue(parsed)
} catch {}
}
return text
.replace(/[`'"]/g, '')
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
const normalizeAssetString = (value: unknown) => splitAssetValue(value).join(',')
const drawingsValue = computed({
get: () => splitAssetValue((formData.value as any).drawings),
set: (value: any) => { ;(formData.value as any).drawings = normalizeAssetString(value) }
})
const operationManualValue = computed({
get: () => splitAssetValue((formData.value as any).operationManual),
set: (value: any) => { ;(formData.value as any).operationManual = normalizeAssetString(value) }
})
const operationVideoValue = computed({
get: () => splitAssetValue((formData.value as any).operationVideo),
set: (value: any) => { ;(formData.value as any).operationVideo = normalizeAssetString(value) }
})
const validateCode = (_rule: any, value: any, callback: any) => {
if (Boolean(formData.value.isCode)) {
callback()
@ -261,6 +330,9 @@ const resetForm = () => {
productName: '',
productIds: [],
images: '',
drawings: '',
operationManual: '',
operationVideo: '',
status: 1,
useTime: 0,
maintainType: undefined,
@ -383,6 +455,9 @@ const submitForm = async () => {
const payload: MoldBrandVO = {
...formData.value,
productIds: Array.isArray(formData.value.productIds) ? formData.value.productIds : [],
drawings: normalizeAssetString((formData.value as any).drawings),
operationManual: normalizeAssetString((formData.value as any).operationManual),
operationVideo: normalizeAssetString((formData.value as any).operationVideo),
isEnable: Boolean(formData.value.isEnable)
}
if (formType.value === 'create') {

@ -303,6 +303,44 @@
@pagination="fetchInstallRecords" />
</div>
</el-tab-pane>
<el-tab-pane :label="t('MoldManagement.MoldBrandDetail.tabDrawings')" name="drawings">
<el-table :data="drawingRows" :stripe="true" :show-overflow-tooltip="true">
<el-table-column :label="t('MoldManagement.MoldBrandDetail.drawings')" prop="url" min-width="180">
<template #default="scope">
<el-image
:src="scope.row.url"
:preview-src-list="drawingRows.map((item) => item.url)"
preview-teleported
fit="cover"
class="mold-brand-asset-image"
/>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!drawingRows.length" :description="t('MoldManagement.MoldBrandDetail.noRecords')" />
</el-tab-pane>
<el-tab-pane :label="t('MoldManagement.MoldBrandDetail.tabOperationManual')" name="operationManual">
<el-table :data="operationManualRows" :stripe="true" :show-overflow-tooltip="true">
<el-table-column :label="t('MoldManagement.MoldBrandDetail.operationManual')" prop="url" min-width="220">
<template #default="scope">
<el-link :href="scope.row.url" target="_blank" type="primary" :underline="false">
{{ scope.row.name }}
</el-link>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!operationManualRows.length" :description="t('MoldManagement.MoldBrandDetail.noRecords')" />
</el-tab-pane>
<el-tab-pane :label="t('MoldManagement.MoldBrandDetail.tabOperationVideo')" name="operationVideo">
<el-table :data="operationVideoRows" :stripe="true" :show-overflow-tooltip="true">
<el-table-column :label="t('MoldManagement.MoldBrandDetail.operationVideo')" prop="url" min-width="260">
<template #default="scope">
<video :src="scope.row.url" controls class="mold-brand-asset-video"></video>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!operationVideoRows.length" :description="t('MoldManagement.MoldBrandDetail.noRecords')" />
</el-tab-pane>
</el-tabs>
</div>
</div>
@ -362,6 +400,9 @@ const maintainLoading = ref(false)
const installLoading = ref(false)
const imageList = computed(() => parseImages(detailData.value?.images))
const drawingRows = computed(() => parseAssetRows(detailData.value?.drawings))
const operationManualRows = computed(() => parseAssetRows(detailData.value?.operationManual))
const operationVideoRows = computed(() => parseAssetRows(detailData.value?.operationVideo))
const lifeRate = computed<number | null>(() => {
const raw = detailData.value?.lifeRate ?? detailData.value?.lifeStatus ?? detailData.value?.useRate
if (raw === undefined || raw === null || raw === '') return null
@ -398,6 +439,42 @@ const parseImages = (value: any): string[] => {
.filter(Boolean)
}
const parseAssetUrls = (value: any): string[] => {
if (!value) return []
if (Array.isArray(value)) return value.flatMap((item) => parseAssetUrls(item))
if (typeof value === 'object' && value.fileUrl) return [String(value.fileUrl).trim()].filter(Boolean)
const text = String(value).trim()
if (!text) return []
if (text.startsWith('{') || text.startsWith('[')) {
try {
return parseAssetUrls(JSON.parse(text))
} catch {}
}
return text
.replace(/[`'"]/g, '')
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
const getAssetName = (url: string) => {
const cleanUrl = String(url || '').split('?')[0]
const idx = cleanUrl.lastIndexOf('/')
const name = idx !== -1 ? cleanUrl.substring(idx + 1) : cleanUrl
try {
return decodeURIComponent(name)
} catch {
return name
}
}
const parseAssetRows = (value: any) => {
return parseAssetUrls(value).map((url) => ({
url,
name: getAssetName(url)
}))
}
const getResultLabel = (value: any) => {
const v = value === '' || value === null || value === undefined ? undefined : String(value)
if (!v) return '-'
@ -974,6 +1051,21 @@ onMounted(() => {
background: var(--el-fill-color-light);
}
.mold-brand-asset-image {
width: 96px;
height: 96px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
}
.mold-brand-asset-video {
width: 280px;
max-width: 100%;
height: 160px;
border-radius: 6px;
background: #000;
}
@media (max-width: 1024px) {
.mold-brand-detail__hero {
grid-template-columns: 1fr;

Loading…
Cancel
Save