2025-10-21 18:37:28 +08:00
|
|
|
|
<template>
|
2025-10-22 14:10:47 +08:00
|
|
|
|
<div>
|
|
|
|
|
|
<el-form ref="form" :model="formData" :rules="rules" label-width="120px">
|
|
|
|
|
|
<el-form-item label="单位名称" prop="companyName">
|
|
|
|
|
|
<el-input v-model="formData.companyName" placeholder="请输入单位名称" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="联系人" prop="contactPerson">
|
|
|
|
|
|
<el-input v-model="formData.contactPerson" placeholder="请输入联系人" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="联系电话" prop="contactPhone">
|
|
|
|
|
|
<el-input v-model="formData.contactPhone" placeholder="请输入联系电话" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="单位地址" prop="address">
|
|
|
|
|
|
<el-input v-model="formData.address" placeholder="请输入单位地址" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<!--
|
|
|
|
|
|
<el-form-item label="状态" prop="status">
|
|
|
|
|
|
<el-switch v-model="formData.status" active-text="启用" inactive-text="禁用" />
|
|
|
|
|
|
</el-form-item> -->
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="文件上传" prop="file">
|
|
|
|
|
|
<el-upload class="upload-demo" :action="uploadFileUrl" :multiple="true" :before-upload="beforeUpload"
|
|
|
|
|
|
:file-list="uploadFileList" :on-success="handleUploadSuccess" :on-error="handleUploadError"
|
|
|
|
|
|
:on-remove="handleRemove" :headers="headers" name="file" accept=".pdf,.doc,.docx,.txt,.jpg,.jpeg,.png"
|
|
|
|
|
|
:on-preview="previewFile">
|
|
|
|
|
|
<el-button size="small" type="primary">点击上传</el-button>
|
|
|
|
|
|
<div slot="tip" class="el-upload__tip">支持多文件上传,文件格式:pdf、doc、docx、txt、jpg、jpeg、png,大小不超过20MB</div>
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<el-form-item label="备注" prop="remark">
|
|
|
|
|
|
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 文件预览对话框 -->
|
2025-10-23 14:58:19 +08:00
|
|
|
|
<el-dialog :title="'文件预览(' + selectFile.fileName + ')'" :visible.sync="isShowFile" width="70%" append-to- >
|
2025-10-22 14:10:47 +08:00
|
|
|
|
<!-- 全屏按钮 -->
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
class="fullscreen-btn"
|
|
|
|
|
|
icon="el-icon-full-screen"
|
|
|
|
|
|
size="mini"
|
|
|
|
|
|
@click="toggleFullScreen"
|
|
|
|
|
|
style="position: absolute; top: 10px; right: 100px; z-index: 10;"
|
|
|
|
|
|
>
|
|
|
|
|
|
全屏
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<div class="preview-container" ref="previewContainer">
|
|
|
|
|
|
<!-- 音频预览 -->
|
|
|
|
|
|
<audio v-if="isAudioType" class="preview-iframe" controls>
|
|
|
|
|
|
<source :src="getPreviewFilePath(selectFile)"/>
|
|
|
|
|
|
</audio>
|
|
|
|
|
|
<!-- 图片预览 -->
|
|
|
|
|
|
<img
|
|
|
|
|
|
v-if="isImageType"
|
|
|
|
|
|
:src="getPreviewFilePath(selectFile)"
|
|
|
|
|
|
class="preview-iframe"
|
|
|
|
|
|
style="max-width: 100%; max-height: 80vh; object-fit: contain;"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- Office文档预览 -->
|
|
|
|
|
|
<iframe
|
|
|
|
|
|
v-if="!isAudioType && !isImageType && !isPdfType && !isTxtType"
|
|
|
|
|
|
:src="fileUrl"
|
|
|
|
|
|
frameborder="0"
|
|
|
|
|
|
class="preview-iframe"
|
|
|
|
|
|
>
|
|
|
|
|
|
</iframe>
|
|
|
|
|
|
<!-- PDF和TXT预览 -->
|
|
|
|
|
|
<iframe
|
|
|
|
|
|
v-if="isPdfType || isTxtType"
|
|
|
|
|
|
:src="getPreviewFilePath(selectFile)"
|
|
|
|
|
|
frameborder="0"
|
|
|
|
|
|
class="preview-iframe"
|
|
|
|
|
|
>
|
|
|
|
|
|
</iframe>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</div>
|
2025-10-21 18:37:28 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-10-22 14:10:47 +08:00
|
|
|
|
import { getAccessToken } from "@/utils/auth";
|
|
|
|
|
|
import ImagePreview from '@/components/ImagePreview';
|
|
|
|
|
|
|
2025-10-21 18:37:28 +08:00
|
|
|
|
export default {
|
|
|
|
|
|
name: 'ChargeCompanyForm',
|
|
|
|
|
|
props: {
|
|
|
|
|
|
// 用于 v-model 的 value(对象)
|
|
|
|
|
|
value: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () => ({})
|
|
|
|
|
|
},
|
|
|
|
|
|
// systemCode 允许字符串或数字
|
|
|
|
|
|
systemCode: {
|
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
|
default: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-10-22 14:10:47 +08:00
|
|
|
|
components: {
|
|
|
|
|
|
ImagePreview
|
|
|
|
|
|
},
|
2025-10-21 18:37:28 +08:00
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
// 本地表单数据 — 保持对象引用(方便 el-form resetFields)
|
|
|
|
|
|
formData: {
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
companyName: null,
|
|
|
|
|
|
contactPerson: null,
|
|
|
|
|
|
contactPhone: null,
|
|
|
|
|
|
address: null,
|
|
|
|
|
|
status: false,
|
|
|
|
|
|
systemCode: '', // 会在 created 中初始化
|
2025-10-22 14:10:47 +08:00
|
|
|
|
remark: null,
|
|
|
|
|
|
file: ''
|
2025-10-21 18:37:28 +08:00
|
|
|
|
},
|
2025-10-22 14:10:47 +08:00
|
|
|
|
// 上传文件列表
|
|
|
|
|
|
uploadFileList: [],
|
|
|
|
|
|
// 文件预览地址
|
|
|
|
|
|
viewFileUrl: process.env.VUE_APP_FILE_API,
|
|
|
|
|
|
// 文件上传地址
|
|
|
|
|
|
uploadFileUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload",
|
|
|
|
|
|
// 上传请求头
|
|
|
|
|
|
headers: { Authorization: "Bearer " + getAccessToken() },
|
|
|
|
|
|
// 正在上传的文件数量
|
|
|
|
|
|
uploadingCount: 0,
|
|
|
|
|
|
// 文件预览相关
|
|
|
|
|
|
isShowFile: false,
|
|
|
|
|
|
selectFile: {
|
|
|
|
|
|
fileName: '',
|
|
|
|
|
|
filePath: '',
|
|
|
|
|
|
isImage: false
|
|
|
|
|
|
},
|
|
|
|
|
|
fileUrl: '',
|
|
|
|
|
|
|
2025-10-21 18:37:28 +08:00
|
|
|
|
// 防止同步回流的标志
|
|
|
|
|
|
isSyncingFromParent: false,
|
|
|
|
|
|
rules: {
|
|
|
|
|
|
companyName: [
|
|
|
|
|
|
{ required: true, message: '单位名称不能为空', trigger: 'blur' }
|
|
|
|
|
|
],
|
|
|
|
|
|
contactPerson: [
|
|
|
|
|
|
{ required: true, message: '联系人不能为空', trigger: 'blur' }
|
|
|
|
|
|
],
|
|
|
|
|
|
contactPhone: [
|
|
|
|
|
|
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
|
|
|
|
|
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
// 当外部传入 systemCode 变化时,更新本地 field(外部 systemCode 优先)
|
|
|
|
|
|
systemCode: {
|
|
|
|
|
|
handler(newVal) {
|
|
|
|
|
|
// 使用 $set 保证响应式(Vue2)
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', newVal === undefined || newVal === null ? '' : newVal)
|
|
|
|
|
|
},
|
|
|
|
|
|
immediate: true
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 监听 value(父组件通过 v-model 传入的数据)变化并同步到本地表单
|
2025-10-22 14:10:47 +08:00
|
|
|
|
value: {
|
|
|
|
|
|
handler(newVal) {
|
|
|
|
|
|
// 标志:正在同步来自父组件的数据,避免触发 emit 回父组件
|
|
|
|
|
|
this.isSyncingFromParent = true
|
2025-10-21 18:37:28 +08:00
|
|
|
|
|
2025-10-22 14:10:47 +08:00
|
|
|
|
if (newVal && Object.keys(newVal).length > 0) {
|
|
|
|
|
|
// 保持 systemCode 优先为当前本地 systemCode(外部 systemCode Prop 会单独处理)
|
|
|
|
|
|
const keepSystemCode = this.formData.systemCode
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 Object.assign 保持 formData 对象引用,避免意外破坏 el-form 的绑定
|
|
|
|
|
|
Object.assign(this.formData, { ...newVal })
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复/保持本地 systemCode(如果你需要让 value 中的 systemCode 覆盖,改这里)
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', keepSystemCode)
|
2025-10-21 18:37:28 +08:00
|
|
|
|
|
2025-10-22 14:10:47 +08:00
|
|
|
|
// 处理文件列表
|
|
|
|
|
|
if (this.formData.file) {
|
|
|
|
|
|
const fileNames = this.formData.file.split(',')
|
|
|
|
|
|
this.uploadFileList = fileNames.map((path, index) => {
|
|
|
|
|
|
// 提取文件名
|
|
|
|
|
|
const fileName = this.extractFileName(path);
|
|
|
|
|
|
// 为每个文件生成唯一的uid
|
|
|
|
|
|
const uid = `local_${index}_${Date.now()}`
|
|
|
|
|
|
// 如果路径已经包含viewFileUrl,则直接使用
|
|
|
|
|
|
const url = path.includes(this.viewFileUrl) ? path : (this.viewFileUrl + path);
|
|
|
|
|
|
return {
|
|
|
|
|
|
uid,
|
|
|
|
|
|
name: fileName,
|
|
|
|
|
|
fileName: fileName,
|
|
|
|
|
|
url: url,
|
|
|
|
|
|
filePath: path, // 保存原始路径用于预览
|
|
|
|
|
|
status: 'success',
|
|
|
|
|
|
isImage: this.isImageExtension(fileName)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.uploadFileList = []
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果 newVal 为空对象或 null,则只清空可编辑字段(保持 systemCode)
|
|
|
|
|
|
const keepSystemCode = this.formData.systemCode
|
|
|
|
|
|
Object.assign(this.formData, {
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
companyName: null,
|
|
|
|
|
|
contactPerson: null,
|
|
|
|
|
|
contactPhone: null,
|
|
|
|
|
|
address: null,
|
|
|
|
|
|
status: false,
|
|
|
|
|
|
remark: null,
|
|
|
|
|
|
file: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', keepSystemCode)
|
|
|
|
|
|
this.uploadFileList = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 等下一个 tick 再允许 emit(保证同步完成后不回流)
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.isSyncingFromParent = false
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
immediate: true,
|
|
|
|
|
|
deep: true
|
|
|
|
|
|
},
|
2025-10-21 18:37:28 +08:00
|
|
|
|
// 本地表单变化时向外 emit(用于 v-model 双向绑定)
|
|
|
|
|
|
// 但如果是我们从父组件同步过来的改动,则不 emit(避免回流)
|
|
|
|
|
|
formData: {
|
|
|
|
|
|
handler(newVal) {
|
|
|
|
|
|
if (this.isSyncingFromParent) {
|
|
|
|
|
|
// 来自父组件的同步,不应再次 emit
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 发出副本,防止父组件直接修改传入对象引用
|
|
|
|
|
|
this.$emit('input', { ...newVal })
|
|
|
|
|
|
},
|
|
|
|
|
|
deep: true
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-10-22 14:10:47 +08:00
|
|
|
|
computed: {
|
|
|
|
|
|
// 判断是否为图片类型
|
|
|
|
|
|
isImageType() {
|
|
|
|
|
|
if (!this.selectFile.fileName) return false;
|
|
|
|
|
|
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp'];
|
|
|
|
|
|
const extension = this.selectFile.fileName.substring(this.selectFile.fileName.lastIndexOf('.')).toLowerCase();
|
|
|
|
|
|
return imageExtensions.includes(extension);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 判断是否为音频类型
|
|
|
|
|
|
isAudioType() {
|
|
|
|
|
|
if (!this.selectFile.fileName) return false;
|
|
|
|
|
|
const audioExtensions = ['.mp3', '.wav', '.ogg', '.flac', '.aac'];
|
|
|
|
|
|
const extension = this.selectFile.fileName.substring(this.selectFile.fileName.lastIndexOf('.')).toLowerCase();
|
|
|
|
|
|
return audioExtensions.includes(extension);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 判断是否为PDF类型
|
|
|
|
|
|
isPdfType() {
|
|
|
|
|
|
if (!this.selectFile.fileName) return false;
|
|
|
|
|
|
return this.selectFile.fileName.toLowerCase().endsWith('.pdf');
|
|
|
|
|
|
},
|
|
|
|
|
|
// 判断是否为TXT类型
|
|
|
|
|
|
isTxtType() {
|
|
|
|
|
|
if (!this.selectFile.fileName) return false;
|
|
|
|
|
|
return this.selectFile.fileName.toLowerCase().endsWith('.txt');
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-10-21 18:37:28 +08:00
|
|
|
|
methods: {
|
|
|
|
|
|
// 验证表单:callback 接收 (valid, fields)
|
|
|
|
|
|
validate(callback) {
|
|
|
|
|
|
if (!this.$refs.form) {
|
|
|
|
|
|
const error = new Error('form ref not found')
|
|
|
|
|
|
if (typeof callback === 'function') callback(false, error)
|
|
|
|
|
|
return Promise.reject(error)
|
|
|
|
|
|
}
|
|
|
|
|
|
// Element UI validate 支持回调或 Promise
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
this.$refs.form.validate((valid, fields) => {
|
|
|
|
|
|
if (typeof callback === 'function') callback(valid, fields)
|
|
|
|
|
|
if (valid) resolve(true)
|
|
|
|
|
|
else reject(fields)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 重置表单验证与字段(保留 systemCode)
|
|
|
|
|
|
resetFields() {
|
|
|
|
|
|
if (!this.$refs.form) return
|
|
|
|
|
|
// resetFields 会根据 model 将字段恢复为 model 中的初始值
|
|
|
|
|
|
this.$refs.form.resetFields()
|
|
|
|
|
|
// 重新确保 systemCode 设置为 props 的值(或空字符串)
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', this.systemCode || '')
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 清除校验提示
|
|
|
|
|
|
clearValidate() {
|
|
|
|
|
|
if (!this.$refs.form) return
|
|
|
|
|
|
this.$refs.form.clearValidate()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 彻底重置 formData(保持 systemCode)
|
|
|
|
|
|
resetFormData() {
|
|
|
|
|
|
const keepSystemCode = this.systemCode || this.formData.systemCode || ''
|
|
|
|
|
|
Object.assign(this.formData, {
|
|
|
|
|
|
id: null,
|
|
|
|
|
|
companyName: null,
|
|
|
|
|
|
contactPerson: null,
|
|
|
|
|
|
contactPhone: null,
|
|
|
|
|
|
address: null,
|
|
|
|
|
|
status: false,
|
2025-10-22 14:10:47 +08:00
|
|
|
|
remark: null,
|
|
|
|
|
|
file: ''
|
2025-10-21 18:37:28 +08:00
|
|
|
|
})
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', keepSystemCode)
|
2025-10-22 14:10:47 +08:00
|
|
|
|
this.uploadFileList = []
|
2025-10-21 18:37:28 +08:00
|
|
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.clearValidate()
|
|
|
|
|
|
// 主动 emit 一次空的表单状态(如果需要通知父组件)
|
|
|
|
|
|
this.$emit('input', { ...this.formData })
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-10-22 14:10:47 +08:00
|
|
|
|
// 文件上传前的钩子函数
|
|
|
|
|
|
beforeUpload(file) {
|
|
|
|
|
|
// 文件大小限制
|
|
|
|
|
|
const isLt20M = file.size / 1024 / 1024 < 20 // 限制20MB
|
|
|
|
|
|
if (!isLt20M) {
|
|
|
|
|
|
this.$message.error('上传文件大小不能超过 20MB!')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示上传中加载状态
|
|
|
|
|
|
if (this.uploadingCount === 0) {
|
|
|
|
|
|
this.$modal.loading("正在上传文件,请稍候...")
|
|
|
|
|
|
}
|
|
|
|
|
|
this.uploadingCount++
|
|
|
|
|
|
return true
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 文件上传成功处理
|
|
|
|
|
|
handleUploadSuccess(res, file, fileList) {
|
|
|
|
|
|
this.uploadingCount--
|
|
|
|
|
|
|
|
|
|
|
|
// 检查响应数据格式
|
|
|
|
|
|
console.log('上传成功响应:', res);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用原始文件名显示
|
|
|
|
|
|
const originalFileName = file.name || '未知文件名';
|
|
|
|
|
|
|
|
|
|
|
|
// 查找当前上传的文件在列表中的索引
|
|
|
|
|
|
const index = this.uploadFileList.findIndex(item => item.uid === file.uid)
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
// 使用服务器返回的文件路径
|
|
|
|
|
|
const filePath = res && res.code === 0 && res.data ? res.data : file.name
|
|
|
|
|
|
|
|
|
|
|
|
// 更新文件列表
|
|
|
|
|
|
this.uploadFileList[index].url = filePath
|
|
|
|
|
|
this.uploadFileList[index].status = 'success'
|
|
|
|
|
|
this.uploadFileList[index].name = originalFileName // 使用原始文件名显示
|
|
|
|
|
|
this.uploadFileList[index].fileName = originalFileName // 添加fileName属性用于显示
|
|
|
|
|
|
this.uploadFileList[index].filePath = filePath // 添加filePath属性用于预览
|
|
|
|
|
|
this.uploadFileList[index].isImage = this.isImageExtension(originalFileName) // 判断是否为图片类型
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果在列表中找不到,添加到列表
|
|
|
|
|
|
const filePath = res && res.code === 0 && res.data ? res.data : file.name
|
|
|
|
|
|
this.uploadFileList.push({
|
|
|
|
|
|
uid: file.uid,
|
|
|
|
|
|
name: originalFileName,
|
|
|
|
|
|
fileName: originalFileName,
|
|
|
|
|
|
url: filePath,
|
|
|
|
|
|
filePath: filePath,
|
|
|
|
|
|
status: 'success',
|
|
|
|
|
|
isImage: this.isImageExtension(originalFileName)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 每次文件上传成功后立即更新file字段
|
|
|
|
|
|
this.updateFilePaths()
|
|
|
|
|
|
|
|
|
|
|
|
// 所有文件上传完成后关闭加载
|
|
|
|
|
|
if (this.uploadingCount === 0) {
|
|
|
|
|
|
this.$modal.closeLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 文件上传失败处理
|
|
|
|
|
|
handleUploadError(err, file, fileList) {
|
|
|
|
|
|
this.uploadingCount--
|
|
|
|
|
|
this.$message.error('文件上传失败,请重试')
|
|
|
|
|
|
|
|
|
|
|
|
if (this.uploadingCount === 0) {
|
|
|
|
|
|
this.$modal.closeLoading()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 文件移除时的处理函数
|
|
|
|
|
|
handleRemove(file, fileList) {
|
|
|
|
|
|
this.uploadFileList = fileList
|
|
|
|
|
|
this.updateFilePaths()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 更新文件路径字符串(以逗号分隔)
|
|
|
|
|
|
updateFilePaths() {
|
|
|
|
|
|
// 使用上传成功后返回的文件路径
|
|
|
|
|
|
const paths = this.uploadFileList
|
|
|
|
|
|
.filter(file => file && (file.status === 'success' || file.url)) // 只处理已成功上传的文件
|
|
|
|
|
|
.map(file => {
|
|
|
|
|
|
// 确保使用正确的文件路径,优先使用url
|
|
|
|
|
|
const filePath = file.url || file.name || ''
|
|
|
|
|
|
|
|
|
|
|
|
// 如果路径包含viewFileUrl,则去掉前缀
|
|
|
|
|
|
if (filePath && this.viewFileUrl && filePath.includes(this.viewFileUrl)) {
|
|
|
|
|
|
return filePath.replace(this.viewFileUrl, '')
|
|
|
|
|
|
}
|
|
|
|
|
|
return filePath
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 设置文件路径,确保总是一个字符串(即使为空)
|
|
|
|
|
|
const filePathStr = paths.join(',') || ''
|
|
|
|
|
|
this.$set(this.formData, 'file', filePathStr)
|
|
|
|
|
|
|
|
|
|
|
|
console.log('更新后的file字段值:', filePathStr)
|
|
|
|
|
|
|
|
|
|
|
|
// 主动触发formData的watch,确保父组件能接收到最新的file值
|
|
|
|
|
|
this.$emit('input', { ...this.formData })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-10-21 18:37:28 +08:00
|
|
|
|
// 外部主动传入数据设置到表单(不会改变 systemCode)
|
|
|
|
|
|
setFormData(data = {}) {
|
|
|
|
|
|
if (!data) return
|
|
|
|
|
|
const keepSystemCode = this.formData.systemCode
|
|
|
|
|
|
// 阻止在同步过程中 emit
|
|
|
|
|
|
this.isSyncingFromParent = true
|
|
|
|
|
|
Object.assign(this.formData, { ...data })
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', keepSystemCode)
|
2025-10-22 14:10:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理文件列表
|
|
|
|
|
|
if (this.formData.file) {
|
|
|
|
|
|
const fileNames = this.formData.file.split(',')
|
|
|
|
|
|
this.uploadFileList = fileNames.map((path, index) => {
|
|
|
|
|
|
// 提取文件名
|
|
|
|
|
|
const fileName = this.extractFileName(path);
|
|
|
|
|
|
// 为每个文件生成唯一的uid
|
|
|
|
|
|
const uid = `local_${index}_${Date.now()}`
|
|
|
|
|
|
// 如果路径已经包含viewFileUrl,则直接使用
|
|
|
|
|
|
const url = path.includes(this.viewFileUrl) ? path : (this.viewFileUrl + path);
|
|
|
|
|
|
return {
|
|
|
|
|
|
uid,
|
|
|
|
|
|
name: fileName,
|
|
|
|
|
|
fileName: fileName,
|
|
|
|
|
|
url: url,
|
|
|
|
|
|
filePath: path, // 保存原始路径用于预览
|
|
|
|
|
|
status: 'success',
|
|
|
|
|
|
isImage: this.isImageExtension(fileName)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.uploadFileList = []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-21 18:37:28 +08:00
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.isSyncingFromParent = false
|
|
|
|
|
|
})
|
2025-10-22 14:10:47 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 判断文件是否为图片格式
|
|
|
|
|
|
isImageExtension(fileName) {
|
|
|
|
|
|
if (!fileName) return false;
|
|
|
|
|
|
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp'];
|
|
|
|
|
|
const extension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
|
|
|
|
|
return imageExtensions.includes(extension);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 获取预览文件路径
|
|
|
|
|
|
getPreviewFilePath(file) {
|
|
|
|
|
|
if (!file || !file.filePath && !file.url) return '';
|
|
|
|
|
|
|
|
|
|
|
|
const filePath = file.filePath || file.url || '';
|
|
|
|
|
|
// 如果是完整的http链接,直接返回
|
|
|
|
|
|
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
|
|
|
|
|
|
return filePath;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 否则拼接完整的图片预览URL
|
|
|
|
|
|
return `${this.viewFileUrl || process.env.VUE_APP_BASE_API}${filePath}`;
|
|
|
|
|
|
},
|
|
|
|
|
|
// 预览文件
|
|
|
|
|
|
previewFile(file) {
|
|
|
|
|
|
console.log('预览文件:', file);
|
|
|
|
|
|
// 设置选中的文件信息
|
|
|
|
|
|
this.selectFile = {
|
|
|
|
|
|
fileName: file.name || file.fileName || '未知文件',
|
|
|
|
|
|
filePath: file.url || file.filePath || '',
|
|
|
|
|
|
isImage: file.isImage || this.isImageExtension(file.name || file.fileName || '')
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 对于Office文档,使用Office Online预览
|
|
|
|
|
|
if (!this.isImageExtension(this.selectFile.fileName) && !this.isPdfType && !this.isTxtType && !this.isAudioType) {
|
|
|
|
|
|
const fileUrl = this.getPreviewFilePath(this.selectFile);
|
|
|
|
|
|
this.fileUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(fileUrl)}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示预览对话框
|
|
|
|
|
|
this.isShowFile = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
// 全屏切换
|
|
|
|
|
|
toggleFullScreen() {
|
|
|
|
|
|
const container = this.$refs.previewContainer;
|
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (!document.fullscreenElement) {
|
|
|
|
|
|
container.requestFullscreen().catch(err => {
|
|
|
|
|
|
console.error(`全屏切换失败: ${err.message}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (document.exitFullscreen) {
|
|
|
|
|
|
document.exitFullscreen();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// 从路径中提取文件名
|
|
|
|
|
|
extractFileName(path) {
|
|
|
|
|
|
if (!path) return '未知文件';
|
|
|
|
|
|
// 移除URL前缀
|
|
|
|
|
|
let cleanPath = path;
|
|
|
|
|
|
if (cleanPath.includes('http://') || cleanPath.includes('https://')) {
|
|
|
|
|
|
const urlObj = new URL(cleanPath);
|
|
|
|
|
|
cleanPath = urlObj.pathname;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 提取文件名
|
|
|
|
|
|
const parts = cleanPath.split('/');
|
|
|
|
|
|
return parts[parts.length - 1] || '未知文件';
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 表单提交前确保file字段已正确设置
|
|
|
|
|
|
ensureFileField() {
|
|
|
|
|
|
this.updateFilePaths()
|
|
|
|
|
|
return this.formData.file
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 表单提交
|
|
|
|
|
|
submitForm() {
|
|
|
|
|
|
this.$refs.form.validate(valid => {
|
|
|
|
|
|
if (valid) {
|
|
|
|
|
|
// 确保file字段已正确设置
|
|
|
|
|
|
this.ensureFileField()
|
|
|
|
|
|
this.$emit('submit', this.formData)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-10-21 18:37:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
// 初始时确保 systemCode 使用 props 值(或空字符串)
|
|
|
|
|
|
this.$set(this.formData, 'systemCode', this.systemCode || '')
|
|
|
|
|
|
// 如果父组件通过 value 传入初始数据,value watcher 的 immediate 会处理同步
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.el-form {
|
|
|
|
|
|
padding: 20px 10px 0 0;
|
|
|
|
|
|
}
|
2025-10-22 14:10:47 +08:00
|
|
|
|
|
2025-10-21 18:37:28 +08:00
|
|
|
|
.el-input,
|
|
|
|
|
|
.el-textarea {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
2025-10-22 14:10:47 +08:00
|
|
|
|
|
|
|
|
|
|
/* 预览容器样式 */
|
|
|
|
|
|
.preview-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 70vh;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.preview-iframe {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 全屏按钮样式 */
|
|
|
|
|
|
.fullscreen-btn {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 10px;
|
|
|
|
|
|
right: 100px;
|
|
|
|
|
|
z-index: 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 修复预览对话框中的样式问题 */
|
|
|
|
|
|
:deep(.el-dialog__body) {
|
|
|
|
|
|
padding: 30px 20px 20px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
2025-10-21 18:37:28 +08:00
|
|
|
|
</style>
|