@@ -14,7 +14,8 @@ | |||
:headers="uploadHeaders" | |||
:show-file-list="false" | |||
:on-error="handleError" | |||
:on-success="handleSuccess"> | |||
:on-success="handleSuccess" | |||
:before-upload="handleBeforeUploadCheck"> | |||
<i class="el-icon-upload"></i> | |||
<div class="el-upload__text"> | |||
将文件拖到此处,或 | |||
@@ -109,6 +110,23 @@ export default { | |||
// 上传前检查文件大小: Boolean | |||
}, | |||
handleBeforeUploadCheck(file) { | |||
if (typeof file !== "object") return false; | |||
if (!("name" in file) || !("type" in file)) return false; | |||
const isXlsx = | |||
file.name.split(".").pop() === "xlsx" && | |||
file.type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; | |||
const isXls = file.name.split(".").pop() === "xls" && file.type === "application/vnd.ms-excel"; | |||
if (isXls || isXlsx) return true; | |||
// false | |||
this.$message({ | |||
message: "仅支持上传 Excel(.xls, .xlsx) 文件", | |||
type: "error", | |||
duration: 1500, | |||
}); | |||
return false; | |||
}, | |||
handleSuccess(response, file, fileList) { | |||
// console.log("success response", response); | |||
@@ -161,8 +161,20 @@ export default function () { | |||
props: [ | |||
{ type: "index", label: "序号" }, | |||
{ prop: "material", label: "物料", isEditField: true }, | |||
{ width: 130, prop: "qty", label: "配方物料重量", filter: (val) => (!!val ? val + " kg" : "-"), isEditField: true }, | |||
{ width: 130, prop: "sumqty", label: "配方总重量", filter: (val) => (!!val ? val + " kg" : "-"), isEditField: true }, | |||
{ | |||
width: 130, | |||
prop: "qty", | |||
label: "配方物料重量", | |||
filter: (val) => (!!val ? val + " kg" : "-"), | |||
isEditField: true, | |||
}, | |||
{ | |||
width: 130, | |||
prop: "sumqty", | |||
label: "配方总重量", | |||
filter: (val) => (!!val ? val + " kg" : "-"), | |||
isEditField: true, | |||
}, | |||
{ prop: "percent", label: "配比 [%]", filter: (val) => (!!val ? val + " %" : "-"), isEditField: true }, | |||
{ prop: "remark", label: "备注", isEditField: true }, | |||
{ prop: "createTime", label: "添加时间", filter: timeFilter }, | |||
@@ -251,7 +263,8 @@ export default function () { | |||
copyUrl: "/pms/bom/copy", | |||
subase: "/pms/bomMaterial", | |||
subpage: "/pms/bomMaterial/page", | |||
importOrderUrl: '/pms/order/importExcelBom', // 导入的api | |||
importUrl: "/pms/order/importExcelBom", // 导入的api | |||
templateUrl: "/importTemplates/bomImport.xlsx", | |||
}, | |||
}; | |||
} |
@@ -1,43 +1,49 @@ | |||
<template> | |||
<el-dialog | |||
class="dialog-just-form" | |||
style="padding: 40px" | |||
style="padding: 32px" | |||
:fullscreen="fullscreen" | |||
:visible="visible" | |||
@close="handleClose" | |||
:destroy-on-close="false" | |||
:close-on-click-modal="configs.clickModalToClose ?? true" | |||
> | |||
:close-on-click-modal="configs.clickModalToClose ?? true"> | |||
<!-- title --> | |||
<div slot="title" class="dialog-title" style="padding: 40px 40px 0"> | |||
<h1 class=""> | |||
<div slot="title" class="dialog-title"> | |||
<h2 class=""> | |||
{{ detailMode ? "查看详情" : dataForm.id ? "修改订单" : "新增订单" }} | |||
</h1> | |||
</h2> | |||
</div> | |||
<!-- form --> | |||
<el-form ref="dataForm" :model="dataForm" v-loading="loadingStatus" style="padding: 0 40px"> | |||
<el-form ref="dataForm" :model="dataForm" v-loading="loadingStatus" size="small"> | |||
<el-row v-for="(row, rowIndex) in configs.form.rows" :key="'row_' + rowIndex" :gutter="20"> | |||
<el-col v-for="(col, colIndex) in row" :key="colIndex" :span="24 / row.length" :class="{ h0: col.hidden }"> | |||
<!-- 通过多个 col === null 可以控制更灵活的 span 大小 --> | |||
<el-form-item v-if="col !== null" :label="col.label" :prop="col.prop" :rules="col.rules || null"> | |||
<el-input v-if="col.input" v-model="dataForm[col.prop]" clearable :disabled="detailMode" v-bind="col.elparams" /> | |||
<el-input | |||
v-if="col.input" | |||
v-model="dataForm[col.prop]" | |||
clearable | |||
:disabled="detailMode" | |||
v-bind="col.elparams" /> | |||
<el-cascader | |||
v-if="col.cascader" | |||
v-model="dataForm[col.prop]" | |||
:options="col.options" | |||
:disabled="detailMode" | |||
v-bind="col.elparams" | |||
></el-cascader> | |||
v-bind="col.elparams"></el-cascader> | |||
<el-select | |||
v-if="col.select" | |||
v-model="dataForm[col.prop]" | |||
clearable | |||
:disabled="detailMode" | |||
v-bind="col.elparams" | |||
@change="handleSelectChange(col, $event)" | |||
> | |||
<el-option v-for="(opt, optIdx) in col.options" :key="'option_' + optIdx" :label="opt.label" :value="opt.value" /> | |||
@change="handleSelectChange(col, $event)"> | |||
<el-option | |||
v-for="(opt, optIdx) in col.options" | |||
:key="'option_' + optIdx" | |||
:label="opt.label" | |||
:value="opt.value" /> | |||
</el-select> | |||
<el-switch | |||
v-if="col.switch" | |||
@@ -45,10 +51,18 @@ | |||
:active-value="col.activeValue ?? 1" | |||
:inactive-value="col.activeValue ?? 0" | |||
@change="handleSwitchChange" | |||
:disabled="detailMode" /> | |||
<el-input | |||
v-if="col.textarea" | |||
type="textarea" | |||
v-model="dataForm[col.prop]" | |||
:disabled="detailMode" | |||
/> | |||
<el-input v-if="col.textarea" type="textarea" v-model="dataForm[col.prop]" :disabled="detailMode" v-bind="col.elparams" /> | |||
<el-date-picker v-if="col.datetime" v-model="dataForm[col.prop]" :disabled="detailMode" v-bind="col.elparams" /> | |||
v-bind="col.elparams" /> | |||
<el-date-picker | |||
v-if="col.datetime" | |||
v-model="dataForm[col.prop]" | |||
:disabled="detailMode" | |||
v-bind="col.elparams" /> | |||
<div class="" v-if="col.component" style="margin: 42px 0 0"> | |||
<!-- 下面这个 component 几乎是为 富文本 quill 定制的了... TODO:后续可能会根据业务需求创建新的版本 --> | |||
@@ -58,8 +72,7 @@ | |||
@update:modelValue="handleComponentModelUpdate(col.prop, $event)" | |||
:modelValue="dataForm[col.prop] ?? ''" | |||
:mode="detailMode ? 'detail' : dataForm.id ? 'edit' : 'create'" | |||
v-bind="col.bind" | |||
/> | |||
v-bind="col.bind" /> | |||
</div> | |||
<!-- add more... --> | |||
</el-form-item> | |||
@@ -68,11 +81,15 @@ | |||
</el-form> | |||
<!-- footer --> | |||
<div slot="footer" style="padding: 0 40px 0"> | |||
<div slot="footer"> | |||
<template v-for="(operate, index) in configs.form.operations"> | |||
<el-button v-if="showButton(operate)" :key="'operation_' + index" :type="operate.type" @click="handleBtnClick(operate)">{{ | |||
operate.label | |||
}}</el-button> | |||
<el-button | |||
v-if="showButton(operate)" | |||
:key="'operation_' + index" | |||
:type="operate.type" | |||
@click="handleBtnClick(operate)"> | |||
{{ operate.label }} | |||
</el-button> | |||
</template> | |||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button> | |||
</div> | |||
@@ -88,8 +105,7 @@ export default { | |||
name: "DialogJustForm", | |||
components: {}, | |||
props: { | |||
configs: { | |||
type: Object, | |||
configs: { type: Object, | |||
default: () => ({ | |||
clickModalToClose: true, | |||
forms: null, | |||
@@ -112,26 +128,34 @@ export default { | |||
if (col.fetchData) | |||
col.fetchData().then(({ data: res }) => { | |||
// console.log("[DialogJustForm fetchData -->]", res.data.list); | |||
if (res.code === 0 && res.data.list) { | |||
if ("injectTo" in col) { | |||
// 保存完整的数据列表 | |||
cachedList[col.prop] = res.data.list; | |||
if (res.code === 0) { | |||
if (typeof res.data === "object" && "list" in res.data) { | |||
if ("injectTo" in col) { | |||
// 保存完整的数据列表,用于自动更新关联字段 | |||
cachedList[col.prop] = res.data.list; | |||
} | |||
this.$set( | |||
col, | |||
"options", | |||
res.data.list.map((i) => ({ | |||
label: col.optionLabel ? i[col.optionLabel] : i.name, | |||
value: col.optionValue ? i[col.optionValue] : i.id, | |||
})) | |||
); | |||
} else if (Array.isArray(res.data)) { | |||
this.$set( | |||
col, | |||
"options", | |||
res.data.map((i) => ({ | |||
label: col.optionLabel ? i[col.optionLabel] : i.name, | |||
value: col.optionValue ? i[col.optionValue] : i.id, | |||
})) | |||
); | |||
} | |||
this.$set( | |||
col, | |||
"options", | |||
res.data.list.map((i) => ({ | |||
label: col.optionLabel ? i[col.optionLabel] : i.name, | |||
value: col.optionValue ? i[col.optionValue] : i.id, | |||
})) | |||
); | |||
// col.options = res.data.list; | |||
} else { | |||
// error 静默失败 | |||
col.options.splice(0); | |||
} | |||
// dataForm[col.prop] = col.default ?? null; // not perfect! | |||
}); | |||
else if (col.fetchTreeData) { | |||
// 获取设备类型时触发的,用于前端构建属性结构,约定,parentId 为0时是顶级节点 | |||
@@ -157,7 +181,7 @@ export default { | |||
cachedList, | |||
detailMode: false, | |||
baseDialogConfig: null, | |||
visible: false | |||
visible: false, | |||
}; | |||
}, | |||
mounted() { | |||
@@ -242,7 +266,7 @@ export default { | |||
/** init **/ | |||
init(id, detailMode) { | |||
this.visible = true | |||
this.visible = true; | |||
// console.log("[DialogJustForm] init", this.dataForm, id, detailMode); | |||
if (this.$refs.dataForm) { | |||
// console.log("[DialogJustForm] clearing form validation..."); | |||
@@ -340,7 +364,9 @@ export default { | |||
const { startTime, endTime } = this.dataForm; | |||
httpPayload = { | |||
...httpPayload, | |||
startTime: startTime ? moment(startTime).format("YYYY-MM-DDTHH:mm:ss") : moment().format("YYYY-MM-DDTHH:mm:ss"), | |||
startTime: startTime | |||
? moment(startTime).format("YYYY-MM-DDTHH:mm:ss") | |||
: moment().format("YYYY-MM-DDTHH:mm:ss"), | |||
endTime: endTime ? moment(endTime).format("YYYY-MM-DDTHH:mm:ss") : moment().format("YYYY-MM-DDTHH:mm:ss"), | |||
}; | |||
} | |||
@@ -420,9 +446,9 @@ export default { | |||
handleClose() { | |||
this.visible = false; | |||
setTimeout(() => { | |||
this.$emit('destroy-dialog') | |||
this.$emit("destroy-dialog"); | |||
// this.resetForm(); | |||
// this.$emit("update:dialogVisible", false); | |||
}, 200); | |||
@@ -4,13 +4,13 @@ | |||
:visible="visible" | |||
@close="handleClose" | |||
width="30%" | |||
title="导入订单" | |||
:title="title" | |||
:destroy-on-close="false" | |||
:close-on-click-modal="configs.clickModalToClose ?? true"> | |||
<el-upload | |||
style="margin-top: 24px; text-align: center" | |||
drag | |||
:action="urls.importOrderUrl" | |||
:action="actionUrl" | |||
:headers="uploadHeaders" | |||
:show-file-list="false" | |||
:on-error="handleError" | |||
@@ -20,11 +20,21 @@ | |||
将文件拖到此处,或 | |||
<em>点击上传</em> | |||
</div> | |||
<div class="el-upload__tip" slot="tip">只能上传 Excel 文件</div> | |||
<div class="el-upload__tip" slot="tip">{{ hint }}</div> | |||
</el-upload> | |||
<div slot="footer"> | |||
<el-button type="success" @click="handleBtnClick({ name: 'download-template' })">下载模板</el-button> | |||
<el-button | |||
type="success" | |||
@click=" | |||
handleBtnClick({ | |||
name: 'download-template', | |||
filename: savedFilename, | |||
url: templateUrl, | |||
}) | |||
"> | |||
下载模板 | |||
</el-button> | |||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button> | |||
</div> | |||
</el-dialog> | |||
@@ -38,6 +48,23 @@ import Cookies from "js-cookie"; | |||
export default { | |||
name: "DialogUpload", | |||
props: { | |||
title: { | |||
type: String, | |||
default: "导入", | |||
}, | |||
hint: { | |||
hint: String, | |||
default: "只能上传 Excel 文件", | |||
}, | |||
filename: { | |||
hint: String, | |||
default: "default_file.xlsx", | |||
}, | |||
url: { | |||
// 下载地址 | |||
hint: String, | |||
default: "/importTemplates/orderImport.xlsx", | |||
}, | |||
configs: { | |||
type: Object, | |||
default: () => ({ | |||
@@ -59,6 +86,15 @@ export default { | |||
token: Cookies.get("token") || "", | |||
}; | |||
}, | |||
actionUrl() { | |||
return window.SITE_CONFIG["apiURL"] + this.urls.importUrl; | |||
}, | |||
savedFilename() { | |||
return "templateUrl" in this.urls ? this.urls.templateUrl.split("/").pop() : this.filename; | |||
}, | |||
templateUrl() { | |||
return "templateUrl" in this.urls ? this.urls.templateUrl : this.url ?? "#"; | |||
}, | |||
}, | |||
methods: { | |||
init(data) { | |||
@@ -75,6 +111,16 @@ export default { | |||
handleSuccess(response, file, fileList) { | |||
// console.log("success response", response); | |||
if ("code" in response && response.code === 500) { | |||
this.$message({ | |||
message: response.msg, | |||
type: "error", | |||
duration: 1500, | |||
}); | |||
return; | |||
} | |||
let message = ""; | |||
let isError = false; | |||
@@ -84,7 +130,7 @@ export default { | |||
message = response; | |||
isError = true; | |||
} | |||
this.handleClose(); | |||
this.$message({ | |||
@@ -105,13 +151,13 @@ export default { | |||
this.handleClose(); | |||
break; | |||
case "download-template": | |||
this.handleDownloadTemplate(); | |||
this.handleDownloadTemplate(payload.filename, payload.url); | |||
break; | |||
} | |||
} | |||
}, | |||
handleDownloadTemplate(filename) { | |||
handleDownloadTemplate(filename, href) { | |||
this.$notify({ | |||
title: "提示", | |||
message: "开始下载,请稍后检查浏览器的下载选项或者下载目录", | |||
@@ -120,7 +166,7 @@ export default { | |||
// 下载模板 | |||
let a = document.createElement("a"); | |||
a.href = "/importTemplates/orderImport.xlsx"; | |||
a.href = href; | |||
a.download = filename ?? "orderTemplate.xlsx"; | |||
document.body.appendChild(a); | |||
a.click(); | |||
@@ -136,7 +182,6 @@ export default { | |||
setTimeout(() => { | |||
this.$emit("destroy-dialog"); | |||
// this.$emit("update:dialogVisible", false); | |||
}, 200); | |||
}, | |||
}, | |||
@@ -48,6 +48,7 @@ | |||
<DialogUpload | |||
ref="uploadDialog" | |||
v-if="uploadDialogVisible" | |||
title="导入订单" | |||
@destroy-dialog="uploadDialogVisible = false" | |||
@refresh-list="handleRefreshList" /> | |||
</section> | |||
@@ -58,7 +59,7 @@ import BaseListTable from "./BaseListTable.vue"; | |||
import BaseSearchForm from "./BaseSearchForm.vue"; | |||
import DialogJustForm from "./DialogJustForm.vue"; | |||
import DialogWithMenu from "./DialogWithMenu.vue"; | |||
import DialogUpload from "./DialogUpload.vue"; | |||
import DialogUpload from "@/components/DialogUpload.vue"; | |||
import moment from "moment"; | |||
// const dictList = JSON.parse(localStorage.getItem("dictList")); | |||
@@ -340,7 +340,7 @@ export default function () { | |||
options: [], | |||
optionLabel: "code", | |||
rules: { required: true, message: "必填项不能为空", trigger: "blur" }, | |||
fetchData: () => this.$http.get("/pms/equipment/page", { params: { limit: 999, page: 1, name: "" } }), | |||
fetchData: () => this.$http.get("/pms/equipment/search", { params: { equipmentTypeCode: 'Press' } }), | |||
elparams: { placeholder: "请选择压机号", filterable: true }, | |||
}, | |||
], | |||
@@ -389,7 +389,7 @@ export default function () { | |||
options: [], | |||
optionLabel: "code", | |||
rules: { required: true, message: "必填项不能为空", trigger: "blur" }, | |||
fetchData: () => this.$http.get("/pms/equipment/page", { params: { limit: 999, page: 1, name: "" } }), | |||
fetchData: () => this.$http.get("/pms/equipment/search", { params: { equipmentTypeCode: 'Mix' } }), | |||
elparams: { placeholder: "请选择混料机号", filterable: true }, | |||
}, | |||
{ | |||
@@ -398,7 +398,7 @@ export default function () { | |||
prop: "kiln", | |||
options: [], | |||
optionLabel: "code", | |||
fetchData: () => this.$http.get("/pms/equipment/page", { params: { limit: 999, page: 1, name: "" } }), | |||
fetchData: () => this.$http.get("/pms/equipment/search", { params: { equipmentTypeCode: 'Kiln' } }), | |||
elparams: { placeholder: "请选择隧道窑号", filterable: true }, | |||
}, | |||
{ | |||