Merge branch 'projects/mes-test' into projects/mes-dy

This commit is contained in:
helloDy 2023-11-27 20:42:33 +08:00
commit c76078214e
16 changed files with 368 additions and 1170 deletions

View File

@ -76,6 +76,7 @@
class="upload-area" class="upload-area"
:class="uploadOpen ? '' : 'height-48'" :class="uploadOpen ? '' : 'height-48'"
ref="uploadArea" ref="uploadArea"
:key="col.prop"
v-if="col.upload"> v-if="col.upload">
<span class="close-icon" :class="uploadOpen ? 'open' : ''"> <span class="close-icon" :class="uploadOpen ? 'open' : ''">
<el-button <el-button
@ -87,13 +88,18 @@
<el-upload <el-upload
class="upload-in-dialog" class="upload-in-dialog"
v-if="col.upload" v-if="col.upload"
:key="col.prop + '__el-upload'"
:action="uploadUrl" :action="uploadUrl"
:headers="uploadHeaders" :headers="uploadHeaders"
:show-file-list="false" :show-file-list="false"
icon="el-icon-upload2" icon="el-icon-upload2"
:disabled="disabled" :disabled="disabled"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:on-success="handleUploadSuccess" :on-success="
(response, file, fileList) => {
handleUploadSuccess(response, file, col.prop);
}
"
v-bind="col.bind"> v-bind="col.bind">
<el-button size="mini" :disabled="col.bind?.disabled || false"> <el-button size="mini" :disabled="col.bind?.disabled || false">
<svg-icon <svg-icon
@ -108,10 +114,10 @@
<uploadedFile <uploadedFile
class="file" class="file"
v-for="file in form[col.prop] || []" v-for="file in form[col.prop]"
:file="file" :file="file"
:key="file.fileUrl" :key="file.fileUrl"
@delete="!disabled && handleDeleteFile(file)" /> @delete="!disabled && handleDeleteFile(file, col.prop)" />
</div> </div>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -152,12 +158,30 @@ const uploadedFile = {
handleDelete() { handleDelete() {
this.$emit('delete', this.file); this.$emit('delete', this.file);
}, },
async handleDownload() {
const data = await this.$axios({
url: this.file.fileUrl,
method: 'get',
responseType: 'blob',
});
await this.$message.success('开始下载');
// create download link
const url = window.URL.createObjectURL(data);
const link = document.createElement('a');
link.href = url;
link.download = this.file.fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
}, },
mounted() {}, mounted() {},
render: function (h) { render: function (h) {
return ( return (
<div <div
title={this.file.fileName} title={this.file.fileName}
onClick={this.handleDownload}
style={{ style={{
background: `url(${tupleImg}) no-repeat`, background: `url(${tupleImg}) no-repeat`,
backgroundSize: '14px', backgroundSize: '14px',
@ -205,7 +229,7 @@ export default {
default: false, default: false,
}, },
hasFiles: { hasFiles: {
type: Boolean, type: Boolean | Array,
default: false, default: false,
}, },
labelPosition: { labelPosition: {
@ -251,7 +275,13 @@ export default {
handler(val) { handler(val) {
this.form = JSON.parse(JSON.stringify(val)); this.form = JSON.parse(JSON.stringify(val));
if (this.hasFiles) { if (this.hasFiles) {
this.form.files = this.form.files ?? []; if (typeof this.hasFiles == 'boolean' && this.hasFiles) {
this.form.files = this.form.files ?? [];
} else if (Array.isArray(this.hasFiles)) {
this.hasFiles.forEach((prop) => {
this.form[prop] = this.form[prop] ?? [];
});
}
} }
}, },
deep: true, deep: true,
@ -348,7 +378,7 @@ export default {
// //
this.form[opt.prop] = response.data; this.form[opt.prop] = response.data;
// dataFormcodebug // dataFormcodebug
this.$emit('update', this.form) this.$emit('update', this.form);
} }
}); });
} }
@ -377,11 +407,12 @@ export default {
// //
beforeUpload() {}, beforeUpload() {},
// bind // bind
handleUploadSuccess(response, file, fileList) { handleUploadSuccess(response, file, prop) {
this.form.files.push({ console.log('[handleUploadSuccess]', response, file, prop);
this.form[prop].push({
fileName: file.name, fileName: file.name,
fileUrl: response.data, fileUrl: response.data,
fileType: 2, fileType: prop == 'files' ? 2 : 1,
}); });
this.$modal.msgSuccess('上传成功'); this.$modal.msgSuccess('上传成功');
this.$emit('update', this.form); this.$emit('update', this.form);
@ -395,8 +426,8 @@ export default {
this.uploadOpen = !this.uploadOpen; this.uploadOpen = !this.uploadOpen;
}, },
handleDeleteFile(file) { handleDeleteFile(file, prop) {
this.form.files = this.form.files.filter( this.form[prop] = this.form[prop].filter(
(item) => item.fileUrl != file.fileUrl (item) => item.fileUrl != file.fileUrl
); );
this.$emit('update', this.form); this.$emit('update', this.form);

View File

@ -124,9 +124,9 @@ export default {
this.Quill = new Quill(editor, this.options); this.Quill = new Quill(editor, this.options);
// start // start
this.$nextTick(() => { this.$nextTick(() => {
this.Quill.blur(); this.Quill?.blur();
if (!this.readOnly) { if (!this.readOnly) {
this.Quill.enable(); this.Quill?.enable();
} }
}); });
// //

View File

@ -1,978 +0,0 @@
<template>
<el-dialog class="dialog-with-menu" :visible="dialogVisible" :destroy-on-close="false" @close="handleClose"
:close-on-click-modal="configs.clickModalToClose ?? true">
<!-- title -->
<div slot="title" class="dialog-title">
<h1 class="">
{{ detailMode ? "查看详情" : dataForm.id ? "编辑" : "新增" }}
</h1>
</div>
<div class="dialog-body__inner relative">
<!-- v-if="dataForm.id && !detailMode && /属性|详情/.test(activeMenu) && $hasPermission()" -->
<el-button v-if="configs.allowAdd ?? (dataForm.id && !detailMode && /属性|详情|参数/.test(activeMenu))" plain
type="primary" size="small" class="at-right-top" style="margin-bottom: 16px" @click="handleAddParam()">+
添加</el-button>
<template v-if="dataForm.id && !detailMode && /附件/.test(activeMenu)">
<el-upload style="position: absolute; width: 100%; height: 0" name="files" :action="uploadUrl"
:show-file-list="false" :headers="uploadHeaders" :on-success="handleUploadSuccess"
:before-upload="handleUploadCheck">
<el-button plain type="primary" size="small" class="at-right-top" style=""> <i class="el-icon-upload"></i> 上传
</el-button>
</el-upload>
</template>
<!-- menu -->
<el-tabs v-model="activeMenu" type="card" @tab-click="handleTabClick">
<!-- <el-tab-pane v-for="(tab, index) in configs.menu" :key="index" :label="tab.name" :name="tab.name"> -->
<el-tab-pane v-for="(tab, index) in actualMenus" :key="index" :name="tab.name">
<span class="slot" slot="label">
<i :class="{
'el-icon-edit': tab.key === 'info',
'el-icon-s-data': tab.key === 'attr',
'el-icon-folder-opened': tab.key === 'attachment',
}"></i>
{{ tab.name }}
</span>
<!-- 表单标签页 -->
<div v-if="tab.key === 'info'">
<!-- form -->
<el-form ref="dataForm" :model="dataForm" v-loading="loadingStatus">
<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">
<el-form-item :label="col.label" :prop="col.prop" :rules="col.rules || null"
v-show="!col.forceDisabled || (col.forceDisabled && dataForm.id)">
<div v-if="col.forceDisabled && dataForm.id" class="force-disabled">
<el-tag :key="col.key" :type="col.type">{{ dataForm[col.prop] || "-" }}</el-tag>
</div>
<el-input v-if="col.input" v-model="dataForm[col.prop]" clearable
:disabled="disableCondition(col.prop)" 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>
<el-select v-if="col.select" v-model="dataForm[col.prop]" clearable
:disabled="disableCondition(col.prop)" 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" />
</el-select>
<el-switch v-if="col.switch" v-model="dataForm[col.prop]" :active-value="1" :inactive-value="0"
@change="handleSwitchChange" :disabled="disableCondition(col.prop)" />
<el-input v-if="col.textarea" type="textarea" v-model="dataForm[col.prop]"
:disabled="disableCondition(col.prop)" v-bind="col.elparams" />
<quillEditor v-if="col.richInput" ref="quill-editor" v-model="dataForm[col.prop]"
:options="col.quillConfig ?? defaultQuillConfig" style="margin-top: 42px"
:disabled="disableCondition(col.prop)" />
<div class="" v-if="col.component" style="margin: 42px 0 0">
<!-- 下面这个 component 几乎是为 富文本 quill 定制的了... TODO后续可能会根据业务需求创建新的版本 -->
<component :is="col.component" :key="'component_' + col.prop"
@update:modelValue="handleComponentModelUpdate(col.prop, $event)" :modelValue="dataForm[col.prop]"
:mode="detailMode ? 'detail' : dataForm.id ? 'edit' : 'create'" />
</div>
<!-- add more... -->
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<!-- 表格标签页 -->
<div v-if="dataForm.id && tab.key === 'attr'" key="attr-list">
<BaseListTable :table-config="null" :column-config="filteredTableProps" :table-data="subList"
@operate-event="handleTableRowOperate" :current-page="attrPage" :current-size="attrSize"
:refresh-layout-key="Math.random()" v-loading="loadingStatus" />
<!-- paginator -->
<el-pagination class="" style="text-align: left" background @size-change="handleSizeChange"
@current-change="handlePageChange" :current-page.sync="attrPage" :page-sizes="[5, 10, 20]"
:page-size="attrSize" :total="attrTotal" layout="total, sizes, prev, next"></el-pagination>
</div>
<!-- 附件标签页 -->
<div v-if="dataForm.id && tab.key === 'attachment'" key="attachment">
<div class="upload-tips" style="font-size: 0.8em; margin-bottom: 12px">文件大小不要超过 2MB</div>
<!-- 附件列表 -->
<div class="" v-loading="loadingStatus">
<ul class="file-list">
<li v-for="(file, index) in fileList" :key="index">
<span class="file-name">{{ file.name }}</span>
<span class="file-operations">
<span class="file-icon preview" @click="handleFileClick('view', file)">
<i class="el-icon-view" style="cursor: pointer"></i>
</span>
<span class="file-icon download" @click="handleFileClick('download', file)">
<i class="el-icon-download" style="color: #0b58ff; cursor: pointer"></i>
</span>
<span class="file-icon delete" @click="handleFileClick('delete', file)">
<i class="el-icon-delete" style="color: red; cursor: pointer"></i>
</span>
</span>
</li>
</ul>
</div>
<!-- img preview dialog -->
<el-dialog key="image-preview-dialog" class="image-preview-dialog" :visible.sync="imgPreviewDialogVisible"
:append-to-body="true">
<div class="img-container">
<img width="100%" :src="currentImgUrl" alt="" />
</div>
</el-dialog>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- sub dialog -->
<small-dialog :append-to-body="true" v-if="showSubDialog" ref="subDialog" :url="urls.subase"
:configs="configs.subDialog" :related-id="dataForm.id" @refreshDataList="getSubList"></small-dialog>
<!-- footer -->
<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)"
:loading="(operate.name === 'add' || operate.name === 'update') && btnLoading">{{ operate.label }}</el-button>
</template>
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
</div>
</el-dialog>
</template>
<script>
import { pick as __pick } from "@/utils/filters";
import SmallDialog from "@/components/SmallDialog.vue";
import BaseListTable from "@/components/BaseListTable.vue";
import Cookies from "js-cookie";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { quillEditor } from "vue-quill-editor";
function reConstructTreeData(listObj) {
const entry = [];
Object.keys(listObj).map((key) => {
const currentNode = listObj[key];
currentNode.label = currentNode.name;
currentNode.value = currentNode.id;
if (currentNode.parentId === "0") {
entry.push(listObj[key]);
return; // return { label: currentNode.name, value: currentNode.id, children: currentNode.children ?? [] };
}
const parentNode = listObj[currentNode.parentId];
if (!parentNode.children) {
parentNode.children = [];
}
parentNode.children.push(currentNode);
});
return entry;
}
export default {
name: "DialogWithMenu",
components: { SmallDialog, BaseListTable, quillEditor },
props: {
configs: {
type: Object,
default: () => ({}),
},
dialogVisible: {
type: Boolean,
default: false,
},
},
inject: ["urls"],
data() {
const dataForm = {};
const autoDisabledQueue = [];
const watingToRefreshQueue = [];
const cached = {}
this.configs.form.rows.forEach((row) => {
row.forEach((col) => {
if (col.upload) dataForm[col.prop] = col.default ?? [];
else dataForm[col.prop] = col.default ?? null;
if (col.autoDisabled) autoDisabledQueue.push(col.prop);
if (!!col.refreshOptionsAfterConfirm) watingToRefreshQueue.push(col);
if (col.fetchData)
col.fetchData().then(({ data: res }) => {
console.log("[Fetch Data]", "list" in res.data, res.data, res.data.list);
if (res.code === 0) {
if (col.cacheFetchedData) {
// cache fetched data
cached[col.prop] = 'list' in res.data ? res.data.list : (Array.isArray(res.data) ? res.data : [])
}
if (!col.options || !col.options.length) {
this.$set(
col,
"options",
"list" in res.data
? res.data.list.map((i) => ({
label: col.optionLabel ? i[col.optionLabel] : i.name,
value: col.optionValue ? i[col.optionValue] : i.id,
}))
: res.data.map((i) => ({
label: col.optionLabel ? i[col.optionLabel] : i.name,
value: col.optionValue ? i[col.optionValue] : i.id,
}))
);
}
// col.options = res.data.list;
else if (col.options.length) {
"list" in res.data ? res.data.list.unshift(...col.options) : res.data.unshift(...col.options);
this.$set(
col,
"options",
"list" in res.data
? res.data.list.map((i) => ({
label: col.optionLabel ? i[col.optionLabel] : i.name,
value: col.optionValue ? i[col.optionValue] : i.id,
}))
: res.data.map((i) => ({
label: col.optionLabel ? i[col.optionLabel] : i.name,
value: col.optionValue ? i[col.optionValue] : i.id,
}))
);
}
} else {
col.options.splice(0);
}
// dataForm[col.prop] = col.default ?? null; // not perfect!
});
else if (col.fetchTreeData) {
// parentId 0
col.fetchTreeData().then(({ data: res }) => {
console.log("[Fetch Tree Data]", res.data);
if (res.code === 0) {
//
const obj = {};
if ("list" in res.data) {
res.data.list.map((item) => {
obj[item.id] = item;
});
} else if (Array.isArray(res.data)) {
res.data.map((item) => {
obj[item.id] = item;
});
}
//
let filteredList = reConstructTreeData(obj);
console.log("** filteredList **", filteredList);
// options
this.$set(col, "options", filteredList);
} else {
col.options.splice(0);
this.$message.error(res.msg);
}
});
}
});
});
return {
// configs,
btnLoading: false,
loadingStatus: false,
activeMenu: this.configs.menu[0].name,
dataForm,
detailMode: false,
autoDisabledQueue,
watingToRefreshQueue,
cached,
showBaseDialog: false,
baseDialogConfig: null,
subList: [],
showSubDialog: false,
disableXXX: false,
defaultQuillConfig: {
modules: {
toolbar: [
[{ font: [] }],
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
["bold", "italic", "underline", "strike"], // toggled buttons
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
["blockquote", "code-block"],
[{ header: 1 }, { header: 2 }], // custom button values
[{ list: "ordered" }, { list: "bullet" }],
// [{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
// [{ 'direction': 'rtl' }], // text direction
// [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
// [{ 'align': [] }],
// ['clean'] // remove formatting button
],
},
theme: "snow",
readOnly: false,
placeholder: "在这里输入描述信息...",
scrollingContainer: null,
},
attrPage: 1,
attrSize: 10,
attrTotal: 0,
fileList: [],
imgPreviewDialogVisible: false,
currentImgUrl: "",
};
},
mounted() {
this.configs.form.rows.forEach((row) => {
row.forEach((col) => {
if (col.changeReflects && typeof col.changeReflects === 'object' && 'fromKey' in col.changeReflects && 'toProp' in col.changeReflects) {
this.$watch(
() => this.dataForm[col.prop],
val => {
if (val && (col.prop in this.cached)) {
console.log("here changeReflects", col.prop, col.changeReflects.toProp, this.cached[col.prop])
if (typeof col.changeReflects.fromKey === 'string') {
this.dataForm[col.changeReflects.toProp] = this.cached[col.prop].find(item => item.id === val)?.[col.changeReflects.fromKey]
} else if (Array.isArray(col.changeReflects.fromKey) && col.changeReflects.delimiter) {
const foundItem = this.dataForm[col.changeReflects.toProp] = this.cached[col.prop].find(item => item.id === val)
if (foundItem) {
const values = col.changeReflects.fromKey.map(key => foundItem[key])
this.dataForm[col.changeReflects.toProp] = values.join(col.changeReflects.delimiter)
} else {
this.dataForm[col.changeReflects.toProp] = col.changeReflects.delimiter
console.log("[DialogWithMenu] mounted() 没找到对应数据")
}
}
}
},
{
immediate: false
}
)
}
});
});
},
watch: {
dialogVisible: function (val) {
if (!!val) {
this.attrPage = 1
this.attrSize = 10
}
},
},
computed: {
actualMenus() {
return this.configs.menu.filter((m) => {
if (m.onlyEditMode && !this.dataForm.id) {
return false;
}
return true;
});
},
filteredTableProps() {
return this.detailMode ? this.configs.table.props.filter((v) => v.prop !== "operations") : this.configs.table.props;
},
uploadHeaders() {
return {
token: Cookies.get("token") || "",
};
},
uploadUrl() {
return this.configs.menu.find((item) => item.key === "attachment")?.actionUrl || "#";
},
},
methods: {
disableCondition(prop) {
return this.detailMode || (this.disableXXX && this.autoDisabledQueue.indexOf(prop) !== -1);
},
/** utitilities */
showButton(operate) {
const notDetailMode = !this.detailMode;
const showAlways = operate.showAlways ?? false;
const editMode = operate.showOnEdit && this.dataForm.id;
const addMode = !operate.showOnEdit && !this.dataForm.id;
const permission = operate.permission ? this.$hasPermission(operate.permission) : true;
const currentMenuKey = this.configs.menu.find((item) => item.name === this.activeMenu)?.key;
return notDetailMode && (showAlways || ((editMode || addMode) && permission)) && currentMenuKey === "info";
},
resetForm(excludeId = false, immediate = false) {
setTimeout(
() => {
Object.keys(this.dataForm).forEach((key) => {
if (excludeId && key === "id") return;
if ("files" in this.dataForm) this.dataForm.files = [];
if ("fileIds" in this.dataForm) this.dataForm.fileIds = [];
else this.dataForm[key] = null;
if (Array.isArray(this.fileList)) {
this.fileList = [];
}
});
this.activeMenu = this.configs.menu[0].name;
this.$refs.dataForm[0].clearValidate();
},
immediate ? 0 : 200
);
},
updateOptions() {
return new Promise((resolve, reject) => {
if (this.watingToRefreshQueue.length) {
this.watingToRefreshQueue.forEach((opt) => {
console.log("[刷新数据, ", opt, "]");
if ("fetchData" in opt) {
opt.fetchData(this.dataForm.id ? this.dataForm.id : -1).then(({ data: res }) => {
if (res.code === 0) {
this.$set(
opt,
"options",
"list" in res.data
? res.data.list.map((i) => ({ label: i.code, value: i.id }))
: res.data.map((i) => ({ label: i.code, value: i.id }))
);
resolve({ done: true });
} else {
this.$message({
message: `${res.code}: ${res.msg}`,
type: "error",
duration: 1500,
});
resolve({ done: false });
}
});
}
});
} else resolve(null);
});
},
/** init **/
init(id, detailMode, menu) {
// this.dialogVisible = true;
if (this.$refs.dataForm && this.$refs.dataForm.length) {
// dialog dataForm [0]
this.$refs.dataForm[0].clearValidate();
}
console.log("[dialog] DialogWithHead init():", id, detailMode);
this.detailMode = detailMode ?? false;
this.$nextTick(() => {
this.dataForm.id = id || null;
if (this.dataForm.id) {
//
this.loadingStatus = true;
//
this.getSubList();
//
this.updateOptions().then((result) => {
if (result === null || (typeof result === "object" && result.done)) {
this.$http.get(this.urls.base + `/${this.dataForm.id}`).then(({ data: res }) => {
if (res && res.code === 0) {
const dataFormKeys = Object.keys(this.dataForm);
this.dataForm = __pick(res.data, dataFormKeys);
if ("files" in res.data) {
console.log("[DialogWithMenu] fileList===>", res.data.files, this.fileList);
/** 返回的文件列表 */
this.fileList = res.data.files
? res.data.files.map((file) => ({
id: file.id,
name: file.fileUrl.split("/").pop(),
url: file.fileUrl,
typeCode: file.typeCode,
}))
: [];
}
}
this.loadingStatus = false;
//
if (menu && menu.key) {
this.activeMenu = this.configs.menu.find((item) => item.key === menu.key)?.name;
}
});
}
});
} else {
//
this.updateOptions();
}
});
},
/** handlers */
handleFileClick(type, file) {
switch (type) {
case "view": {
//
this.$http
.get("/pms/attachment/downloadFile", {
params: {
attachmentId: file.id,
type: 0, // 0 1
},
responseType: "blob",
})
.then(({ data: res }) => {
console.log("preivew", res);
if (/image/i.test(res.type)) {
//
this.currentImgUrl = URL.createObjectURL(res);
this.imgPreviewDialogVisible = true;
} else if (/pdf/i.test(res.type)) {
// pdf
let a = document.createElement('a')
a.setAttribute('target', '_blank')
a.href = URL.createObjectURL(res)
a.click()
console.log('before remove a ', a)
a.remove()
console.log('removed a ', a)
} else {
this.$message({
message: "非图片和PDF文件请下载后预览",
type: "error",
duration: 1500,
});
}
});
break;
}
case "download": {
//
this.$http
.get("/pms/attachment/downloadFile", {
params: {
attachmentId: file.id,
type: 1, // 0 1
},
responseType: "blob",
})
.then(({ data: res }) => {
const blob = new Blob([res]);
/** 通知 */
this.$notify({
title: "成功",
message: "开始下载",
type: "success",
duration: 1200,
});
if ("download" in document.createElement("a")) {
const alink = document.createElement("a");
alink.download = file.name;
alink.style.display = "none";
alink.target = "_blank";
alink.href = URL.createObjectURL(blob);
document.body.appendChild(alink);
alink.click();
URL.revokeObjectURL(alink.href);
document.body.removeChild(alink);
} else {
navigator.msSaveBlob(blob, fileName);
}
});
break;
}
case "delete": {
return this.$confirm(`确定删除图片: ${file.name}`, "提示", {
confirmButtonText: "确认",
cancelButtonText: "我再想想",
type: "warning",
})
.then(() => {
this.loadingStatus = true;
const newFilelist = this.fileList.filter((f) => f.id !== file.id);
this.updateRemoteFiles(newFilelist).then((msg) => {
if (msg && msg === "success") {
this.fileList = newFilelist;
this.loadingStatus = false;
/** 通知 */
this.$notify({
title: "成功",
message: "已删除",
type: "success",
duration: 1200,
});
}
});
})
.catch((err) => { });
}
}
},
handleUploadSuccess(response, file, fileList) {
console.log("[DialogWithMenu] uploadedFileList", response, file, fileList);
if (response.code === 0) {
const uploadedFile = response.data[0];
const fileItem = {
id: uploadedFile.id,
name: uploadedFile.fileUrl.split("/").pop(),
url: uploadedFile.fileUrl,
typeCode: file.typeCode,
};
this.loadingStatus = true;
this.updateRemoteFiles([...this.fileList, fileItem]).then((msg) => {
if (msg && msg === "success") {
this.fileList.push(fileItem);
this.loadingStatus = false;
/** 通知 */
this.$notify({
title: "成功",
message: "上传成功",
type: "success",
duration: 1200,
});
}
});
}
},
updateRemoteFiles(filelist) {
return this.$http
.put("/pms/product", {
id: "id" in this.dataForm ? this.dataForm.id : "DEFAULT_ID",
fileIds: filelist.map((f) => f.id),
})
.then(({ data: res }) => {
if (res.code === 0) return "success";
});
},
handleUploadCheck(file) {
console.log("[before upload]", file);
const LIMIT = 2 * 1024 * 1024; // bytes
if (file.size > LIMIT) {
this.$message({
message: "文件大小不能超过 2MB",
type: "error",
duration: 1500,
});
return false;
} else return true;
},
handleComponentModelUpdate(propName, { subject, payload: { data } }) {
this.dataForm[propName] = JSON.stringify(data);
console.log("[DialogJustForm] handleComponentModelUpdate", this.dataForm[propName]);
},
handleSelectChange(col, eventValue) {
console.log("[dialog] select change: ", col, eventValue);
},
handleSwitchChange(val) {
console.log("[dialog] switch change: ", val, this.dataForm);
},
handleBtnClick(payload) {
console.log("btn click payload: ", payload);
if ("name" in payload) {
switch (payload.name) {
case "cancel":
this.handleClose();
break;
case "reset":
this.resetForm(true, true); // true means exclude id
break;
case "add":
case "update": {
this.$refs.dataForm[0].validate((passed, result) => {
if (passed) {
//
this.btnLoading = true
this.loadingStatus = true;
const method = payload.name === "add" ? "POST" : "PUT";
//
const hasAttachment = !!this.configs.menu.find((item) => item.key === "attachment");
if (hasAttachment) {
const fileIds = this.fileList.map((item) => item.id);
this.$set(this.dataForm, "fileIds", fileIds);
}
// id
let extraIds = {};
if (this.configs.extraIds && typeof this.configs.extraIds === "object") {
// extraIds
Object.entries(this.configs.extraIds).forEach(([key, value]) => {
extraIds[key] = value;
});
}
//
this.btnLoading = true
this.$http({
url: this.urls.base,
method,
data: {
...extraIds,
...this.dataForm,
},
})
.then(({ data: res }) => {
this.btnLoading = false
this.loadingStatus = false;
console.log("[add&update] res is: ", res);
if (res.code === 0) {
this.$message.success(payload.name === "add" ? "添加成功" : "更新成功");
this.$emit("refreshDataList");
// watingToRefreshQueue
// if (this.watingToRefreshQueue.length) {
// //
// this.watingToRefreshQueue.forEach((opt) => {
// console.log("[, ", opt, "]");
// if ("fetchData" in opt) {
// opt.fetchData().then(({ data: res }) => {
// if (res.code === 0) {
// this.$set(
// opt,
// "options",
// "list" in res.data
// ? res.data.list.map((i) => ({ label: i.name, value: i.id }))
// : res.data.map((i) => ({ label: i.name, value: i.id }))
// );
// }
// });
// }
// });
// }
this.handleClose();
} else {
this.$message({
message: `${res.code}: ${res.msg}`,
type: "error",
duration: 2000,
});
if (this.btnLoading) this.btnLoading = false
}
})
.catch((errMsg) => {
this.$message.error("参数错误:" + errMsg);
if (this.loadingStatus) this.loadingStatus = false;
if (this.btnLoading) this.btnLoading = false
});
} else {
this.$message.error("请核查字段信息");
}
});
}
}
}
},
handleTabClick(payload) {
// console.log("tab click payload: ", this.activeMenu);
// if (this.activeMenu === this.configs.menu[1].name) {
// //
// this.getSubList();
// }
},
handlePageChange(val) {
this.getSubList(val, this.attrSize);
},
handleSizeChange(val) {
this.attrPage = 1;
this.attrSize = val
this.getSubList(1, val);
},
getSubList(page, size) {
const params = {};
params.page = page ?? this.attrPage;
params.limit = size ?? this.attrSize;
const requiredParams = this.configs.table.extraParams;
if (requiredParams) {
if (Array.isArray(requiredParams)) {
requiredParams.forEach((str) => {
if (/id/i.test(str)) {
params[str] = this.dataForm.id;
} else {
params[str] = "";
}
});
} else if (typeof requiredParams === "string") {
//
params[this.configs.table.extraParams] = this.dataForm.id;
// dataForm.id
}
}
this.$http.get(this.urls.subpage, { params }).then(({ data: res }) => {
console.log("[DialogWithMenu] getSubList:", res);
if (res.code === 0 && res.data?.list) {
//
this.subList = res.data.list;
this.attrTotal = res.data.total;
} else {
this.subList.splice(0);
this.attrTotal = 0;
}
});
},
handleAddParam(id) {
this.showSubDialog = true;
this.$nextTick(() => {
this.$refs.subDialog.init(id ?? null);
});
},
handleClose() {
this.resetForm();
this.$emit("update:dialogVisible", false);
},
/** 列表handlers */
handleAddItem() {
// console.log('[dialog] handleAddItem ot list...');
this.showBaseDialog = true;
this.$nextTick(() => {
console.log("[sub-dialog] ", this.$refs["sub-dialog"].init());
});
},
handleTableRowOperate({ type, data }) {
console.log("handleTableRowOperate", type, data);
switch (type) {
case "delete": {
//
console.log('delete....', data)
const itemName = typeof data === 'object' ? (data.attrName || data.name || data.material || data.id) : data
return this.$confirm(`是否删除条目: ${itemName}`, "提示", {
confirmButtonText: "确认",
cancelButtonText: "我再想想",
type: "warning",
})
.then(() => {
// this.$http.delete(this.urls.base + `/${data}`).then((res) => {
this.$http({
url: this.urls.subase,
method: "DELETE",
data: [`${data.id}`],
}).then(({ data: res }) => {
if (res.code === 0) {
this.$message.success({
message: "删除成功!",
duration: 1000,
onClose: () => {
this.getSubList(1, 20);
},
});
}
});
})
.catch((err) => { });
}
case "edit": {
this.handleAddParam(data); /** data is ==> id */
break;
}
// case 'view-detail-action':
// this.openDialog(data, true);
// break;
}
},
},
};
</script>
<style scoped>
.el-menu {
margin: 16px 0 !important;
}
.el-menu.el-menu--horizontal {
border: none !important;
/* background: #0f02 !important; */
}
/* .el-menu--horizontal > .el-menu-item.is-active { */
/* border-bottom-color: #0b58ff; */
/* } */
.dialog-with-menu>>>.el-dialog__body {
/* padding-top: 16px !important;
padding-bottom: 16px !important; */
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.el-select,
.el-cascader {
width: 100% !important;
}
.dialog-with-menu>>>.el-dialog__header {
padding: 10px 20px 10px;
/* background: linear-gradient(to bottom, rgba(0, 0, 0, 0.25), white); */
}
.relative {
position: relative;
}
.at-right-top {
position: absolute;
top: 0;
right: 0;
z-index: 10000;
}
ul.file-list,
ul.file-list>li {
padding: 0;
margin: 0;
list-style: none;
}
.file-list {
max-height: 20vh;
overflow-y: auto;
margin-top: 12px;
/* width: 240px; */
display: flex;
flex-direction: column;
}
ul.file-list>li {
border-radius: 4px;
background-color: #edededd2;
padding: 8px;
margin-bottom: 2px;
display: flex;
justify-content: space-between;
}
ul.file-list>li:hover {
background-color: #ededed;
}
.file-operations {
display: flex;
}
.file-icon {
margin-right: 8px;
font-size: 16px;
line-height: 1;
display: flex;
place-content: center;
width: 16px;
height: 16px;
}
/* .image-preview-dialog {
} */
.force-disabled {
margin-top: 42px;
}
</style>

View File

@ -46,35 +46,36 @@
}}文件大小不超过2MB }}文件大小不超过2MB
</div> </div>
</el-upload> </el-upload>
<!-- <div
class="file-list__item"
v-for="n in 9"
:key="n"
:style="{
display: n > 4 && !expand ? 'none' : 'block',
}"
:data-name="n"
:class="{ 'default-icon': !isPicMode }">
<i class="el-icon-delete"></i>
</div> -->
<div <div
class="file-list__item"
v-for="(file, index) in files" v-for="(file, index) in files"
:key="file.fileName" :key="file.fileName"
:style="{ style="width: 100%">
background: isPicMode <div
? `url(${file.fileUrl}) no-repeat` class="file-list__item"
: `url(${defaultBg}) no-repeat`, v-if="!isPicMode"
backgroundSize: isPicMode ? '100% 100%' : '64px', :style="{
backgroundPosition: isPicMode ? '0% 0%' : 'center', background: isPicMode
}" ? `url(${file.fileUrl}) no-repeat`
:data-name="file.fileName"> : `url(${defaultBg}) no-repeat`,
<el-button backgroundSize: isPicMode ? '100% 100%' : '64px',
v-if="!disabled" backgroundPosition: isPicMode ? '0% 0%' : 'center',
type="text" }"
class="el-icon-delete" @click="handleDownload(file)"
style="padding: 0" :data-name="file.fileName">
@click="(e) => handleDelete(file)" /> <el-button
v-if="!disabled"
type="text"
class="el-icon-delete"
style="padding: 0"
@click="(e) => handleDelete(file)" />
</div>
<el-image
v-else
class="file-list__item"
style="width: 100%"
:src="file.fileUrl"
:preview-src-list="files.map((item) => item.fileUrl)"></el-image>
</div> </div>
</section> </section>
</div> </div>
@ -189,6 +190,32 @@ export default {
}, 500); }, 500);
}, },
async handleDownload(file) {
if (this.isPicMode) {
// this.$emit('preview', file);
const link = document.createElement('a');
link.href = file.fileUrl;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
// this.$emit('download', file);
const data = await this.$axios({
url: file.fileUrl,
method: 'get',
responseType: 'blob',
});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(new Blob([data]));
link.download = file.fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
},
emitFilelist() { emitFilelist() {
this.$emit('update', this.files); this.$emit('update', this.files);
}, },

View File

@ -42,16 +42,26 @@
v-model="form" /> v-model="form" />
</div> </div>
<div v-if="section.key == 'attrs'" style="margin-top: 12px"> <div
v-if="section.key == 'attrs'"
style="margin-top: 12px; position: relative">
<div
v-if="!mode.includes('detail')"
style="position: absolute; top: -40px; right: 0">
<el-button @click="handleAddAttr" type="text">
<i class="el-icon-plus"></i>
添加属性
</el-button>
</div>
<base-table <base-table
v-loading="attrListLoading" v-loading="attrListLoading"
:table-props="section.props" :table-props="section.props"
:page="attrQuery?.params.pageNo || 1" :page="attrQuery?.params.pageNo || 1"
:limit="attrQuery?.params.pageSize || 10" :limit="attrQuery?.params.pageSize || 10"
:table-data="list" :table-data="list"
:add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr"
@emitFun="handleEmitFun"> @emitFun="handleEmitFun">
<!-- :add-button-show="mode.includes('detail') ? null : '添加属性'"
@emitButtonClick="handleAddAttr" -->
<method-btn <method-btn
v-if="section.tableBtn" v-if="section.tableBtn"
slot="handleBtn" slot="handleBtn"
@ -76,7 +86,7 @@
<el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit"> <el-button v-if="mode == 'detail'" type="primary" @click="toggleEdit">
编辑 编辑
</el-button> </el-button>
<el-button v-else type="primary" @click="handleConfirm">确定</el-button> <el-button v-else type="primary" @click="handleConfirm">保存</el-button>
<!-- sections的第二项必须是 属性列表 --> <!-- sections的第二项必须是 属性列表 -->
<!-- <el-button <!-- <el-button
v-if="sections[1].allowAdd" v-if="sections[1].allowAdd"
@ -162,7 +172,9 @@ export default {
input: true, input: true,
label: '属性名称', label: '属性名称',
prop: 'name', prop: 'name',
rules: [{ required: true, message: '属性名称不能为空', trigger: 'blur' }], rules: [
{ required: true, message: '属性名称不能为空', trigger: 'blur' },
],
}, },
], ],
[ [

View File

@ -1,29 +1,62 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<SearchBar :formConfigs="searchBarFormConfig" ref="search-bar" @headBtnClick="handleSearchBarBtnClick" /> <SearchBar
:formConfigs="searchBarFormConfig"
ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" />
<!-- 列表 --> <!-- 列表 -->
<base-table :table-props="tableProps" :page="queryParams.pageNo" :limit="queryParams.pageSize" :table-data="list" <base-table
:table-props="tableProps"
:page="queryParams.pageNo"
:limit="queryParams.pageSize"
:table-data="list"
@emitFun="handleEmitFun"> @emitFun="handleEmitFun">
<method-btn v-if="tableBtn.length" slot="handleBtn" :width="120" label="操作" :method-list="tableBtn" <method-btn
v-if="tableBtn.length"
slot="handleBtn"
:width="120"
label="操作"
:method-list="tableBtn"
@clickBtn="handleTableBtnClick" /> @clickBtn="handleTableBtnClick" />
</base-table> </base-table>
<!-- 分页组件 --> <!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" <pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize"
@pagination="getList" /> @pagination="getList" />
<!-- 对话框(添加) --> <!-- 对话框(添加) -->
<base-dialog :dialogTitle="title" :dialogVisible="open" @close="cancel" @cancel="cancel" width="60%" <base-dialog
:dialogTitle="title"
:dialogVisible="open"
@close="cancel"
@cancel="cancel"
width="60%"
@confirm="submitForm"> @confirm="submitForm">
<DialogForm v-if="open" key="index-dialog-form" ref="form" label-position="top" size="small" v-model="form" <DialogForm
v-if="open"
key="index-dialog-form"
ref="form"
label-position="top"
size="small"
v-model="form"
:has-files="['files', 'files2']"
:rows="computedRows" /> :rows="computedRows" />
</base-dialog> </base-dialog>
<!-- 设备 详情 - 编辑 --> <!-- 设备 详情 - 编辑 -->
<EquipmentDrawer v-if="editVisible" ref="drawer" :mode="editMode" @update-mode="editMode = $event" <EquipmentDrawer
:data-id="form.id" :sections="[ v-if="editVisible"
ref="drawer"
:mode="editMode"
@update-mode="editMode = $event"
:data-id="form.id"
:sections="[
{ {
name: '基本信息', name: '基本信息',
key: 'base', key: 'base',
@ -50,20 +83,23 @@
tableBtn: [ tableBtn: [
this.$auth.hasPermi('base:core-equipment-attr:update') this.$auth.hasPermi('base:core-equipment-attr:update')
? { ? {
type: 'edit', type: 'edit',
btnName: '修改', btnName: '修改',
} }
: undefined, : undefined,
this.$auth.hasPermi('base:core-equipment-attr:delete') this.$auth.hasPermi('base:core-equipment-attr:delete')
? { ? {
type: 'delete', type: 'delete',
btnName: '删除', btnName: '删除',
} }
: undefined, : undefined,
].filter((v) => v), ].filter((v) => v),
allowAdd: true, allowAdd: true,
}, },
]" @refreshDataList="getList" @cancel="cancelEdit" @destroy="cancelEdit" /> ]"
@refreshDataList="getList"
@cancel="cancelEdit"
@destroy="cancelEdit" />
</div> </div>
</template> </template>
@ -96,21 +132,21 @@ export default {
tableBtn: [ tableBtn: [
this.$auth.hasPermi(`base:core-equipment:update`) this.$auth.hasPermi(`base:core-equipment:update`)
? { ? {
type: 'detail', type: 'detail',
btnName: '详情', btnName: '详情',
} }
: undefined, : undefined,
this.$auth.hasPermi('base:core-equipment:update') this.$auth.hasPermi('base:core-equipment:update')
? { ? {
type: 'edit', type: 'edit',
btnName: '修改', btnName: '修改',
} }
: undefined, : undefined,
this.$auth.hasPermi('base:core-equipment:delete') this.$auth.hasPermi('base:core-equipment:delete')
? { ? {
type: 'delete', type: 'delete',
btnName: '删除', btnName: '删除',
} }
: undefined, : undefined,
].filter((v) => v), ].filter((v) => v),
tableProps: [ tableProps: [
@ -176,14 +212,18 @@ export default {
type: 'separate', type: 'separate',
}, },
{ {
type: this.$auth.hasPermi('base:core-equipment:export') ? 'button' : '', type: this.$auth.hasPermi('base:core-equipment:export')
? 'button'
: '',
btnName: '导出', btnName: '导出',
name: 'export', name: 'export',
plain: true, plain: true,
color: 'primary', color: 'primary',
}, },
{ {
type: this.$auth.hasPermi('base:core-equipment:create') ? 'button' : '', type: this.$auth.hasPermi('base:core-equipment:create')
? 'button'
: '',
btnName: '新增', btnName: '新增',
name: 'add', name: 'add',
plain: true, plain: true,
@ -196,7 +236,9 @@ export default {
input: true, input: true,
label: '设备名称', label: '设备名称',
prop: 'name', prop: 'name',
rules: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }], rules: [
{ required: true, message: '设备名称不能为空', trigger: 'blur' },
],
// bind: { // bind: {
// disabled: this.editMode == 'detail', // some condition, like detail mode... // disabled: this.editMode == 'detail', // some condition, like detail mode...
// } // }
@ -225,7 +267,9 @@ export default {
label: '设备类型', label: '设备类型',
prop: 'equipmentTypeId', prop: 'equipmentTypeId',
url: '/base/core-equipment-type/page?pageNo=1&pageSize=100', url: '/base/core-equipment-type/page?pageNo=1&pageSize=100',
rules: [{ required: true, message: '设备类型不能为空', trigger: 'blur' }], rules: [
{ required: true, message: '设备类型不能为空', trigger: 'blur' },
],
bind: { bind: {
filterable: true, filterable: true,
}, },
@ -235,7 +279,11 @@ export default {
label: '预计生产时间(min/天)', label: '预计生产时间(min/天)',
prop: 'workTime', prop: 'workTime',
rules: [ rules: [
{ required: true, message: '预计生产时间不能为空', trigger: 'blur' }, {
required: true,
message: '预计生产时间不能为空',
trigger: 'blur',
},
{ {
type: 'number', type: 'number',
message: '请输入正确的数字值', message: '请输入正确的数字值',
@ -291,7 +339,11 @@ export default {
label: '单件产品加工时间(s)', label: '单件产品加工时间(s)',
prop: 'processingTime', prop: 'processingTime',
rules: [ rules: [
{ required: true, message: '单件产品加工时间不能为空', trigger: 'blur' }, {
required: true,
message: '单件产品加工时间不能为空',
trigger: 'blur',
},
{ {
type: 'number', type: 'number',
message: '请输入正确的数字值', message: '请输入正确的数字值',
@ -322,13 +374,19 @@ export default {
[ [
{ {
upload: true, upload: true,
label: '上传资料', label: '设备资料',
prop: 'files', prop: 'files',
}, },
], ],
[ [
{ input: true, label: '备注', prop: 'remark' } {
upload: true,
label: '设备图片',
prop: 'files2',
fileType: 1,
},
], ],
[{ input: true, label: '备注', prop: 'remark' }],
// [ // [
// { // {
// assetUpload: true, // assetUpload: true,
@ -429,7 +487,7 @@ export default {
// //
form: { form: {
id: null, id: null,
files: [] files: [],
}, },
showUploadComponents: false, // showUploadComponents: false, //
}; };
@ -441,36 +499,36 @@ export default {
computedRows() { computedRows() {
return this.showUploadComponents return this.showUploadComponents
? [ ? [
...this.rows, ...this.rows,
[ [
{ {
assetUpload: true, assetUpload: true,
key: 'eq-assets', // key: 'eq-assets', //
label: '上传资料', label: '上传资料',
fieldName: 'assets', fieldName: 'assets',
subcomponent: AssetsUpload, subcomponent: AssetsUpload,
prop: 'uploadedAssets', prop: 'uploadedAssets',
default: [], default: [],
bind: { bind: {
'is-pic-mode': false, 'is-pic-mode': false,
},
}, },
}, ],
], [
[ {
{ assetUpload: true,
assetUpload: true, key: 'eq-pics', //
key: 'eq-pics', // label: '上传图片',
label: '上传图片', fieldName: 'images',
fieldName: 'images', subcomponent: AssetsUpload,
subcomponent: AssetsUpload, // prop: '',
// prop: '', // default: [],
// default: [], bind: {
bind: { 'is-pic-mode': true,
'is-pic-mode': true, },
}, },
}, ],
], ]
]
: this.rows; : this.rows;
}, },
}, },
@ -512,7 +570,8 @@ export default {
spec: undefined, spec: undefined,
description: undefined, description: undefined,
remark: undefined, remark: undefined,
files: [] files: [],
files2: [],
}; };
this.resetForm('form'); this.resetForm('form');
}, },
@ -540,9 +599,12 @@ export default {
if (!valid) { if (!valid) {
return; return;
} }
const payload = Object.assign({}, this.form);
payload.files = [...payload.files, ...payload.files2];
delete payload.files2;
// //
if (this.form.id != null) { if (this.form.id != null) {
updateEquipment(this.form).then((response) => { updateEquipment(payload).then((response) => {
this.$modal.msgSuccess('修改成功'); this.$modal.msgSuccess('修改成功');
this.open = false; this.open = false;
this.getList(); this.getList();
@ -550,7 +612,7 @@ export default {
return; return;
} }
// //
createEquipment(this.form).then((response) => { createEquipment(payload).then((response) => {
this.$modal.msgSuccess('新增成功'); this.$modal.msgSuccess('新增成功');
this.open = false; this.open = false;
this.getList(); this.getList();
@ -569,7 +631,7 @@ export default {
this.getList(); this.getList();
this.$modal.msgSuccess('删除成功'); this.$modal.msgSuccess('删除成功');
}) })
.catch(() => { }); .catch(() => {});
}, },
/** 导出按钮操作 */ /** 导出按钮操作 */
handleExport() { handleExport() {
@ -587,7 +649,7 @@ export default {
this.$download.excel(response, '设备.xls'); this.$download.excel(response, '设备.xls');
this.exportLoading = false; this.exportLoading = false;
}) })
.catch(() => { }); .catch(() => {});
}, },
// //
viewDetail(id) { viewDetail(id) {

View File

@ -8,11 +8,11 @@
<template> <template>
<div class="app-container allow-overflow"> <div class="app-container allow-overflow">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<small-title <!-- <small-title
style="margin: 16px 0; padding-left: 8px" style="margin: 16px 0; padding-left: 8px"
:no-padding="true"> :no-padding="true">
设备运行状态 设备运行状态
</small-title> </small-title> -->
<div <div
class="graph" class="graph"
@ -22,7 +22,8 @@
flex-direction: column; flex-direction: column;
position: relative; position: relative;
"> ">
<div class="blue-title">各设备加工数量</div> <!-- <div class="blue-title">各设备加工数量</div> -->
<div class="blue-title">设备运行状态</div>
<div class="legend"> <div class="legend">
<div class="legend-item"> <div class="legend-item">
<span class="icon blue"></span> <span class="icon blue"></span>
@ -41,22 +42,17 @@
<span class="text">速度开动率</span> <span class="text">速度开动率</span>
</div> </div>
</div> </div>
<div class="graph-grid"> <div v-if="list.length" class="graph-grid">
<div class="bg-grid grid-line"> <div class="bg-grid grid-line">
<div <div class="grid-item" v-for="item in list.length" :key="item"></div>
class="grid-item"
v-for="item in list.length"
:key="item"></div>
</div> </div>
<div class="bg-grid grid-charts"> <div class="bg-grid grid-charts">
<pie-chart <pie-chart v-for="item in list" :key="item.id" :value="item" />
v-for="item in list"
:key="item.id"
:value="item" />
<!-- <pie-chart v-for="item in 5" :key="item" :value="item" /> --> <!-- <pie-chart v-for="item in 5" :key="item" :value="item" /> -->
</div> </div>
</div> </div>
<div class="no-data-bg" v-else></div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -202,7 +202,9 @@ export default {
]; ];
this.config.series[1].data = [ this.config.series[1].data = [
{ name: '速度开动率', value: peEfficiency }, { name: '速度开动率', value: peEfficiency },
{ name: '', value: Math.ceil(peEfficiency) - peEfficiency }, { name: '', value: 100 },
// { name: '', value: peEfficiency },
// { name: '', value: Math.ceil(peEfficiency) - peEfficiency },
]; ];
// //
this.textData = { this.textData = {

View File

@ -21,18 +21,15 @@ export default {
chart: null, chart: null,
}; };
}, },
// watch: { watch: {
// list: { list: {
// handler(listdata) { handler(listdata) {
// if (listdata && listdata.length) { this.setOption();
// console.log('[linechart] list changed', listdata); },
// const option = this.handleList(listdata); immediate: true,
// this.setOption(option); deep: true,
// } },
// }, },
// immediate: true,
// },
// },
computed: { computed: {
option() { option() {
const opt = []; const opt = [];
@ -55,7 +52,7 @@ export default {
}, },
formatter: (params) => { formatter: (params) => {
const name = params[0].name; const name = params[0].name;
const goodRate = opt.find((item) => item[0] == name)[4]; const goodRate = opt.find((item) => item[0] == name)[4] || '0';
return ` return `
<h1 style="font-size: 18px; letter-spacing: 1px;">${ <h1 style="font-size: 18px; letter-spacing: 1px;">${
params[0].axisValue params[0].axisValue
@ -109,6 +106,9 @@ export default {
xAxis: { xAxis: {
type: 'category', type: 'category',
axisTick: { show: false }, axisTick: { show: false },
axisLabel: {
rotate: 45,
},
data: opt.map((item) => item[0]), data: opt.map((item) => item[0]),
}, },
yAxis: { yAxis: {

View File

@ -6,15 +6,15 @@
--> -->
<template> <template>
<div class="app-container"> <div class="app-container" style="flex: 1; height: 1px; display: flex; flex-direction: column;">
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<SearchBar <SearchBar
:formConfigs="searchBarFormConfig" :formConfigs="searchBarFormConfig"
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<el-row> <el-row type="flex" style="flex: 1;">
<el-col class="custom-tabs"> <el-col class="custom-tabs" style="flex: 1;">
<el-tabs <el-tabs
v-model="activeName" v-model="activeName"
:stretch="true" :stretch="true"
@ -32,7 +32,7 @@
<div <div
v-if="activeName == 'graph'" v-if="activeName == 'graph'"
class="graph" class="graph"
style="height: 40vh; display: flex; flex-direction: column"> style="height: 100%; display: flex; flex-direction: column">
<div class="blue-title">各设备加工数量</div> <div class="blue-title">各设备加工数量</div>
<LineChart v-if="list && list.length" :list="list" /> <LineChart v-if="list && list.length" :list="list" />
<div v-else class="no-data-bg"></div> <div v-else class="no-data-bg"></div>
@ -63,13 +63,13 @@ export default {
activeName: 'table', // defaults to 'table' activeName: 'table', // defaults to 'table'
searchBarFormConfig: [ searchBarFormConfig: [
// //
{ // {
__index: 'product', // __index: 'product',
type: 'select', // type: 'select',
label: '产品', // label: '',
placeholder: '请选择产品', // placeholder: '',
param: 'productId', // param: 'productId',
}, // },
// 线 // 线
{ {
__index: 'line', __index: 'line',
@ -156,17 +156,17 @@ export default {
{ {
// width: 160, // width: 160,
prop: 'inQuantity', prop: 'inQuantity',
label: '进片数量', label: '加工数量',
}, },
{ {
// width: 160, // width: 160,
prop: 'outQuantity', prop: 'outQuantity',
label: '出片数量', label: '合格数量',
}, },
{ {
// width: 160, // width: 160,
prop: 'nokQuantity', prop: 'nokQuantity',
label: '破损/不合格数', label: '不合格数',
}, },
{ {
// width: 160, // width: 160,
@ -377,6 +377,9 @@ export default {
} }
:deep(.custom-tabs) { :deep(.custom-tabs) {
.el-tabs {
height: 100%;
}
.el-tabs__header { .el-tabs__header {
margin-bottom: 8px; margin-bottom: 8px;
display: inline-block; display: inline-block;
@ -389,6 +392,14 @@ export default {
line-height: 36px !important; line-height: 36px !important;
height: 36px; height: 36px;
} }
.el-tabs__content {
height: calc(100% - 48px);
}
#pane-graph {
height: 100%;
}
} }
.blue-title { .blue-title {

View File

@ -7,27 +7,30 @@
--> -->
<template> <template>
<div class="alarm-handle"> <div class="alarm-handle">
<DialogForm <el-skeleton v-if="loading" />
ref="orderForm" <div v-else>
key="orderForm" <DialogForm
v-model="orderForm" ref="orderForm"
:disabled="readOnly" key="orderForm"
:has-files="false" v-model="orderForm"
label-position="top" :disabled="readOnly"
:rows="orderFormRows" /> :has-files="false"
label-position="top"
:rows="orderFormRows" />
<small-title style="margin: 16px 0" :no-padding="true" size="sm"> <small-title style="margin: 16px 0" :no-padding="true" size="sm">
处理方式 处理方式
</small-title> </small-title>
<DialogForm <DialogForm
key="handleMethodForm" key="handleMethodForm"
ref="handleMethodForm" ref="handleMethodForm"
v-model="handleMethodForm" v-model="handleMethodForm"
:disabled="readOnly" :disabled="readOnly"
:has-files="true" :has-files="true"
label-position="top" label-position="top"
:rows="handleMethodFormRows" /> :rows="handleMethodFormRows" />
</div>
</div> </div>
</template> </template>
@ -43,6 +46,7 @@ export default {
components: { SmallTitle, DialogForm, Editor }, components: { SmallTitle, DialogForm, Editor },
data() { data() {
return { return {
loading: false,
orderForm: { orderForm: {
id: null, id: null,
equipment: null, equipment: null,
@ -82,6 +86,9 @@ export default {
label: '处理人', label: '处理人',
prop: 'hander', prop: 'hander',
url: '/base/core-worker/listAll', url: '/base/core-worker/listAll',
bind: {
multiple: true,
},
rules: [ rules: [
{ required: true, message: '类型名称不能为空', trigger: 'blur' }, { required: true, message: '类型名称不能为空', trigger: 'blur' },
], ],
@ -122,9 +129,14 @@ export default {
}; };
}, },
mounted() { mounted() {
this.getDict().then(() => { this.loading = true;
this.init(); this.getDict()
}); .then(() => {
this.init();
})
.catch((err) => {
this.loading = false;
});
}, },
methods: { methods: {
/** /**
@ -139,8 +151,9 @@ export default {
* 初始化 * 初始化
*/ */
async init() { async init() {
this.initTop(); await this.initTop();
this.initDown(); await this.initDown();
this.loading = false;
}, },
/** /**
@ -172,16 +185,19 @@ export default {
this.$msgError('缺少报警日志id'); this.$msgError('缺少报警日志id');
this.$emit('close'); this.$emit('close');
} }
const url = '/base/equipment-alarm-hand/get'; const url = '/base/equipment-alarm-hand/page'; // page
const { data, code } = await this.$axios({ const { data, code } = await this.$axios({
url: url, url: url,
method: 'get', method: 'get',
params: { params: {
id: this.logId, logId: this.logId,
}, },
}); });
if (code == 0) { if (code == 0) {
this.handleMethodForm = data; this.handleMethodForm = {
...data.list[0],
hander: data.list[0]?.hander?.split(',') || '',
};
} }
}, },
@ -213,7 +229,11 @@ export default {
const { code, data } = await this.$axios({ const { code, data } = await this.$axios({
url: url + (this.handleMethodForm.id ? '/update' : '/create'), url: url + (this.handleMethodForm.id ? '/update' : '/create'),
method: this.handleMethodForm.id ? 'put' : 'post', method: this.handleMethodForm.id ? 'put' : 'post',
data: { ...this.handleMethodForm, logId: this.logId }, data: {
...this.handleMethodForm,
hander: this.handleMethodForm.hander?.join(',') || '',
logId: this.logId,
},
}); });
if (code == 0) { if (code == 0) {
return true; return true;

View File

@ -42,7 +42,8 @@
:dataForm="form" :dataForm="form"
:rows="formRows" /> --> :rows="formRows" /> -->
<el-row v-if="mode.includes('detail')" style="margin-bottom: 24px"> <!-- <el-row v-if="mode.includes('detail')" style="margin-bottom: 24px"> -->
<el-row style="margin-bottom: 24px">
<el-col :span="8"> <el-col :span="8">
<div <div
class="title" class="title"
@ -62,7 +63,7 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-row v-else style="margin-bottom: 24px" :gutter="20"> <!-- <el-row v-else style="margin-bottom: 24px" :gutter="20">
<el-form ref="form" :model="form"> <el-form ref="form" :model="form">
<el-col :span="8"> <el-col :span="8">
<el-form-item <el-form-item
@ -85,7 +86,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-form> </el-form>
</el-row> </el-row> -->
</div> </div>
<div <div
@ -132,8 +133,8 @@
<el-button <el-button
type="primary" type="primary"
v-if="!mode.includes('detail')" v-if="!mode.includes('detail')"
@click="handleSave"> @click="handleCancel">
保存 确定
</el-button> </el-button>
</div> </div>
</div> </div>

View File

@ -26,7 +26,7 @@ export default {
color: ['#288AFF'], color: ['#288AFF'],
grid: { grid: {
top: 64, top: 64,
left: 56, left: '8%',
right: 64, right: 64,
bottom: 56, bottom: 56,
}, },

View File

@ -67,15 +67,20 @@
class="app-container equipment-process-amount" class="app-container equipment-process-amount"
style="flex: 1; border-radius: 8px; background: #fff"> style="flex: 1; border-radius: 8px; background: #fff">
<!-- main area --> <!-- main area -->
<div class="main-content" style="display: flex; flex-direction: column"> <div
class="main-content"
style="height: 100%; display: flex; flex-direction: column">
<SearchBar <SearchBar
:formConfigs="searchBarFormConfig" :formConfigs="searchBarFormConfig"
ref="search-bar" ref="search-bar"
@headBtnClick="handleSearchBarBtnClick" /> @headBtnClick="handleSearchBarBtnClick" />
<el-row> <el-row style="flex: 1">
<el-col class="custom-tabs"> <el-col class="custom-tabs" style="height: 100%">
<el-tabs v-model="activeName" @tab-click="handleTabClick"> <el-tabs
v-model="activeName"
@tab-click="handleTabClick"
style="height: 100%">
<el-tab-pane :label="'\u2002数据列表\u2002'" name="table"> <el-tab-pane :label="'\u2002数据列表\u2002'" name="table">
<base-table <base-table
v-if="mode == 'table'" v-if="mode == 'table'"
@ -93,7 +98,9 @@
</base-table> </base-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="'\u3000柱状图\u3000'" name="graph"> <el-tab-pane :label="'\u3000柱状图\u3000'" name="graph">
<div class="graph" style="height: 56vh"> <div
class="graph"
style="height: 100%;">
<!-- graph --> <!-- graph -->
<Graph <Graph
v-if="list.length" v-if="list.length"
@ -546,6 +553,13 @@ li {
.el-tree-node__content { .el-tree-node__content {
padding: 8px 24px !important; padding: 8px 24px !important;
} }
.custom-tabs >>> .el-tabs__content {
height: calc(100% - 42px);
}
.custom-tabs >>> .el-tab-pane {
height: 100%;
}
</style> </style>
<style> <style>

View File

@ -17,7 +17,7 @@
<method-btn <method-btn
v-if="tableBtn.length" v-if="tableBtn.length"
slot="handleBtn" slot="handleBtn"
:width="320" :width="240"
label="操作" label="操作"
:method-list="tableBtn" :method-list="tableBtn"
@clickBtn="handleClick" @clickBtn="handleClick"

View File

@ -73,7 +73,7 @@ const tableProps = [
{ {
prop: 'status', prop: 'status',
label: '订单状态', label: '订单状态',
filter: publicFormatter('order_priority') filter: publicFormatter('order_status')
}, },
{ {
prop: 'startProduceTime', prop: 'startProduceTime',
@ -88,7 +88,7 @@ const tableProps = [
minWidth: 160 minWidth: 160
}, },
{ {
prop: 'productLines', prop: 'lineNames',
label: '加工线', label: '加工线',
filter: (val) => val ? val.join(',') : '', filter: (val) => val ? val.join(',') : '',
minWidth: 180 minWidth: 180