@@ -270,7 +270,6 @@ export default { | |||
inject: ["urls"], | |||
data() { | |||
const dataForm = {}; | |||
let dataForm__duplicate = null; | |||
const autoDisabledQueue = []; | |||
const watingToRefreshQueue = []; | |||
const cached = {}; | |||
@@ -374,15 +373,12 @@ export default { | |||
}); | |||
}); | |||
if (this.configs.form.duplicate) dataForm__duplicate = JSON.parse(JSON.stringify(dataForm)); | |||
return { | |||
// configs, | |||
btnLoading: false, | |||
loadingStatus: false, | |||
activeMenu: this.configs.menu[0].name, | |||
dataForm, | |||
dataForm__duplicate, | |||
detailMode: false, | |||
autoDisabledQueue, | |||
watingToRefreshQueue, | |||
@@ -423,44 +419,44 @@ export default { | |||
}; | |||
}, | |||
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, | |||
// } | |||
// ); | |||
// } | |||
// }); | |||
// }); | |||
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: { | |||
@@ -782,17 +778,7 @@ export default { | |||
console.log("[DialogJustForm] handleComponentModelUpdate", this.dataForm[propName]); | |||
}, | |||
handleSelectChange(col, eventValue) { | |||
console.log("[dialog] select change: ", col, eventValue, this.dataForm__duplicate); | |||
if (this.dataForm__duplicate !== null) { | |||
console.log("before B--------->", this.dataForm__duplicate); | |||
const shape = this.cached[col.prop].find((item) => item.id === eventValue); | |||
this.$set( | |||
this.dataForm__duplicate, | |||
col.changeReflects.toProp, | |||
shape && col.changeReflects.toProp in shape ? shape[col.changeReflects.toProp] : null | |||
); | |||
console.log("B--------->", shape, this.dataForm__duplicate); | |||
} | |||
console.log("[dialog] select change: ", col, eventValue); | |||
}, | |||
handleSwitchChange(val) { | |||
console.log("[dialog] switch change: ", val, this.dataForm); | |||
@@ -812,8 +798,6 @@ export default { | |||
case "update": { | |||
this.$refs.dataForm[0].validate((passed, result) => { | |||
if (passed) { | |||
// 判断dataForm类型 | |||
let dataForm = this.dataForm__duplicate !== null ? this.dataForm__duplicate : this.dataForm; | |||
// 如果通过验证 | |||
this.btnLoading = true; | |||
this.loadingStatus = true; | |||
@@ -823,8 +807,7 @@ export default { | |||
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); | |||
dataForm.fileIds = fileIds; | |||
this.$set(this.dataForm, "fileIds", fileIds); | |||
} | |||
// 加载额外需要的 id | |||
@@ -845,8 +828,6 @@ export default { | |||
// console.log('actualPayload', actualPayload); | |||
// } | |||
console.log("before update:", dataForm); | |||
// 实际发送请求 | |||
this.btnLoading = true; | |||
this.$http({ | |||
@@ -854,8 +835,7 @@ export default { | |||
method, | |||
data: { | |||
...extraIds, | |||
// ...this.dataForm, | |||
...dataForm, | |||
...this.dataForm, | |||
}, | |||
}) | |||
.then(({ data: res }) => { | |||
@@ -11,7 +11,7 @@ export default { | |||
}, | |||
data() { | |||
return { | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"], | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"], | |||
}; | |||
}, | |||
render: function (h) { | |||
@@ -20,7 +20,7 @@ export default function () { | |||
prop: "statusDictValue", | |||
label: "订单状态", | |||
filter: (val) => | |||
val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-", | |||
val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-", | |||
}, | |||
// { prop: "startTime", label: "开始时间" }, | |||
// { prop: "shapeCode", label: "砖型" }, | |||
@@ -13,7 +13,7 @@ export default function () { | |||
{ | |||
prop: "statusDictValue", | |||
label: "订单状态", | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), | |||
}, | |||
{ prop: "bomCode", label: "配方" }, | |||
{ width: 120, prop: "qty", label: "混料总量 [kg]" }, | |||
@@ -49,7 +49,7 @@ export default function () { | |||
{ width: 60, prop: "orderCate", label: "子号" }, | |||
{ width: 160, prop: "code", label: "压制订单号" }, | |||
{ width: 60, prop: "percent", label: "进度", filter: (val) => (val !== null && val !== undefined ? val + " %" : "-") }, | |||
{ prop: "statusDictValue", label: "订单状态", filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), }, | |||
{ prop: "statusDictValue", label: "订单状态", filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), }, | |||
{ prop: "startTime", label: "开始时间" }, | |||
{ width: 100, prop: "shapeCode", label: "砖型" }, | |||
{ prop: "pressCode", label: "压机" }, | |||
@@ -97,7 +97,7 @@ export default { | |||
width: 575, | |||
prop: "statusDictValue", | |||
label: "订单状态", | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), | |||
}, | |||
{ prop: "qty", label: "混料总量 [kg]" }, | |||
{ | |||
@@ -121,7 +121,7 @@ export default { | |||
{ | |||
prop: "statusDictValue", | |||
label: "订单状态", | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), | |||
}, | |||
{ prop: "qty", label: "生产量" }, | |||
{ prop: "qtyComplete", label: "完成量" }, | |||
@@ -81,7 +81,7 @@ export default function () { | |||
}, | |||
data() { | |||
return { | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"], | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"], | |||
}; | |||
}, | |||
render: function (h) { | |||
@@ -97,7 +97,7 @@ export default { | |||
width: 575, | |||
prop: "statusDictValue", | |||
label: "订单状态", | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), | |||
filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), | |||
}, | |||
{ prop: "qty", label: "混料总量 [kg]" }, | |||
{ | |||
@@ -233,7 +233,7 @@ export default function () { | |||
}, | |||
data() { | |||
return { | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"], | |||
orderStatusMap: ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"], | |||
}; | |||
}, | |||
render: function (h) { | |||
@@ -19,7 +19,7 @@ export default function () { | |||
// { | |||
// prop: "statusDictValue", | |||
// label: "订单状态", | |||
// filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝"][val] : "-"), | |||
// filter: (val) => (val !== null && val !== undefined ? ["等待", "确认", "生产", "暂停", "结束", "接受", "拒绝", "已下发"][val] : "-"), | |||
// }, | |||
// { width: 120, prop: "startTime", label: "开始时间" }, | |||
{ width: 120, prop: "shapeCode", label: "砖型" }, | |||
@@ -0,0 +1,996 @@ | |||
<template> | |||
<el-dialog | |||
class="dialog-with-menu" | |||
:visible="visible" | |||
:destroy-on-close="false" | |||
@close="handleClose" | |||
@closed="$emit('destroy')" | |||
:close-on-click-modal="configs.clickModalToClose ?? false"> | |||
<!-- 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> | |||
<!-- menu --> | |||
<el-tabs v-model="activeMenu" type="card"> | |||
<!-- <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="formLoading || optionsLoading"> | |||
<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"> | |||
<span>{{ opt.label }}</span> | |||
<span v-if="col.customLabel" style="display: inline-clock; margin-left: 12px; font-size: 0.9em"> | |||
{{ opt[col.customLabel] || "无描述" }} | |||
</span> | |||
</el-option> | |||
</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" /> | |||
<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="tableLoading" /> | |||
<!-- paginator --> | |||
<el-pagination | |||
class="" | |||
style="text-align: left" | |||
background | |||
@size-change="handleSizeChange" | |||
@current-change="handlePageChange" | |||
:current-page.sync="attrPage" | |||
:page-sizes="[10, 20, 50]" | |||
: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="filelistLoading"> | |||
<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> | |||
</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"; | |||
export default { | |||
name: "DialogWithMenu", | |||
components: { SmallDialog, BaseListTable }, | |||
props: { | |||
configs: { | |||
type: Object, | |||
default: () => ({}), | |||
}, | |||
}, | |||
inject: ["urls"], | |||
data() { | |||
const dataForm = {}; | |||
const requestList = []; | |||
const autoDisabledQueue = []; | |||
const refreshQueue = []; | |||
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) refreshQueue.push(col); | |||
if (col.fetchData) requestList.push(col); | |||
}); | |||
}); | |||
return { | |||
visible: false, | |||
btnLoading: false, | |||
formLoading: false, | |||
optionsLoading: false, | |||
tableLoading: false, | |||
filelistLoading: false, | |||
activeMenu: this.configs.menu[0].name, | |||
dataForm, | |||
detailMode: false, | |||
autoDisabledQueue, | |||
refreshQueue, | |||
cached, | |||
showBaseDialog: false, | |||
baseDialogConfig: null, | |||
subList: [], | |||
showSubDialog: false, | |||
attrPage: 1, | |||
attrSize: 20, | |||
attrTotal: 0, | |||
fileList: [], | |||
watchList: [], // 在编辑中,根据这个列表依次调用 handleWatch 依次监听事件 | |||
requestList, | |||
}; | |||
}, | |||
mounted() { | |||
this.initWatchList(); | |||
}, | |||
watch: { | |||
dialogVisible: function (val) { | |||
if (!!val) { | |||
this.attrPage = 1; | |||
this.attrSize = 20; | |||
} | |||
}, | |||
}, | |||
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; | |||
}, | |||
}, | |||
methods: { | |||
/** | |||
* @description: 从配置项中提取出需要监听的项 | |||
*/ | |||
initWatchList() { | |||
console.log("[DWM initWatchList]"); | |||
this.configs.form.rows.forEach((row) => { | |||
row.forEach((col) => { | |||
/** 找到配置项中含有 changeReflects 属性的项 */ | |||
if ( | |||
col.changeReflects && | |||
typeof col.changeReflects === "object" && | |||
"fromKey" in col.changeReflects && | |||
"toProp" in col.changeReflects | |||
) { | |||
/** 保存一份数据结构到待监听列表 */ | |||
this.watchList.push({ | |||
targetProp: col.prop, | |||
fromProp: col.changeReflects.fromKey, | |||
followerProp: col.changeReflects.toProp, | |||
delimiter: col.changeReflects.delimiter, | |||
}); | |||
} | |||
}); | |||
}); | |||
}, | |||
/** | |||
* @description: 开始监听事件 | |||
*/ | |||
startWatchProcess() { | |||
console.log("[DWM startWatchProcess]"); | |||
if (this.watchList.length) { | |||
this.watchList.forEach((item) => { | |||
this.handleWatch(item.targetProp, item.followerProp, this.cached); | |||
}); | |||
} | |||
}, | |||
/** | |||
* | |||
* @param {string} targetProp 监听哪一个prop | |||
* @param {string} followerProp 哪个prop跟随改变 | |||
* @param {object} dataSource 缓存的数据源 | |||
*/ | |||
handleWatch(targetProp, followerProp, dataSource) { | |||
this.$watch( | |||
() => this.dataForm[targetProp], | |||
(val) => { | |||
if (val && targetProp in dataSource) { | |||
this.dataForm[followerProp] = dataSource[targetProp]?.find((item) => item.id === val)?.[followerProp]; | |||
} | |||
}, | |||
{ | |||
immediate: true, | |||
} | |||
); | |||
}, | |||
/** | |||
* | |||
* @param {string} id ID | |||
* @param {boolean} detailMode 是否是详情模式 | |||
*/ | |||
async init(id, detailMode, menu) { | |||
this.detailMode = detailMode ?? false; | |||
this.dataForm.id = id || null; | |||
this.visible = true; | |||
await this.doRequests(); | |||
this.$nextTick(() => { | |||
if (this.dataForm.id) { | |||
// 提前获取属性列表 | |||
this.getSubList(); | |||
// 获取详情 | |||
this.getDetail(this.dataForm.id, menu); | |||
} else { | |||
// 如果不是编辑 | |||
this.startWatchProcess(); | |||
// this.updateSelectOptions(); | |||
} | |||
}); | |||
}, | |||
/** 准备选择器的列表 */ | |||
async doRequests() { | |||
console.log("[DWM doRequests] requestList", this.requestList); | |||
if (this.requestList.length) { | |||
const promiseList = []; | |||
this.optionsLoading = true; | |||
try { | |||
this.requestList.forEach((opt) => { | |||
console.log("[DWM doRequests]", opt.fetchData); | |||
promiseList.push(async function () { | |||
// const { data: res } = await opt.fetchData(this.dataForm.id ? this.dataForm.id : -1); | |||
const { data: res } = await opt.fetchData(); | |||
if (opt.cacheFetchedData) this.cached[opt.prop] = "list" in res.data ? res.data.list : res.data || []; | |||
console.log("[DWM doRequests] res", res); | |||
if (res.code === 0) { | |||
this.$set( | |||
opt, | |||
"options", | |||
"list" in res.data | |||
? "customLabel" in opt | |||
? res.data.list.map((i) => ({ | |||
label: opt.optionLabel ? i[opt.optionLabel] : i.name, | |||
value: opt.optionValue ? i[opt.optionValue] : i.id, | |||
[opt.customLabel]: i[opt.customLabel], | |||
})) | |||
: res.data.list.map((i) => ({ | |||
label: opt.optionLabel ? i[opt.optionLabel] : i.name, | |||
value: opt.optionValue ? i[opt.optionValue] : i.id, | |||
})) | |||
: "customLabel" in opt | |||
? res.data.map((i) => ({ | |||
label: opt.optionLabel ? i[opt.optionLabel] : i.name, | |||
value: opt.optionValue ? i[opt.optionValue] : i.id, | |||
[opt.customLabel]: i[opt.customLabel], | |||
})) | |||
: res.data.map((i) => ({ | |||
label: opt.optionLabel ? i[opt.optionLabel] : i.name, | |||
value: opt.optionValue ? i[opt.optionValue] : i.id, | |||
})) | |||
); | |||
console.log("[DWM doRequests] set options", opt); | |||
} else { | |||
// res.code != 0 | |||
this.$set(opt, "options", []); | |||
} | |||
}); | |||
}); | |||
const v = await Promise.all(promiseList.map(fn => fn.call(this))); | |||
console.log("v", v); | |||
this.optionsLoading = false; | |||
return true; | |||
} catch (err) { | |||
this.$message.err("刷新选项失败"); | |||
this.optionsLoading = false; | |||
return false; | |||
} | |||
} | |||
}, | |||
/** | |||
* 获取详情信息,和参数列表 | |||
* @param {string} id ID | |||
*/ | |||
async getDetail(id, menu) { | |||
if (id) { | |||
this.formLoading = true; | |||
try { | |||
this.$http.get(this.urls.base + `/${id}`).then(({ data: res }) => { | |||
if (res && res.code === 0) { | |||
const dataFormKeys = Object.keys(this.dataForm); | |||
console.log("[DWM getDetail] dataFormKeys -------->", dataFormKeys); | |||
this.dataForm = __pick(res.data, dataFormKeys); | |||
if ("files" in res.data) { | |||
console.log("[DWM getDetail] 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.startWatchProcess(); | |||
this.formLoading = false; | |||
// 是否要跳转到附件页 | |||
if (menu && menu.key) { | |||
this.activeMenu = this.configs.menu.find((item) => item.key === menu.key)?.name; | |||
} | |||
}); | |||
} catch (err) { | |||
this.formLoading = false; | |||
return false; | |||
} | |||
} | |||
this.formLoading = false; | |||
return false; | |||
}, | |||
/** | |||
* 获取参数列表 | |||
* @param {number} page 页码 | |||
* @param {number} size 每页条数 | |||
*/ | |||
getSubList(page, size) { | |||
this.tableLoading = true; | |||
const params = { | |||
page: page ?? this.attrPage, | |||
limit: size ?? this.attrSize, | |||
}; | |||
/** 检查 this.configs.table 是否有 extraParams 选项 */ | |||
if ("extraParams" in this.configs.table) { | |||
if (Array.isArray(this.configs.table.extraParams)) { | |||
this.configs.table.extraParams.forEach((prop) => { | |||
if (/id/i.test(prop)) { | |||
params[prop] = this.dataForm.id; | |||
} else { | |||
params[prop] = ""; | |||
} | |||
}); | |||
} else if (typeof this.configs.table.extraParams === "string") { | |||
params[this.configs.table.extraParams] = this.dataForm.id; | |||
} | |||
} | |||
/** 获取请求 */ | |||
this.$http.get(this.urls.subpage, { params }).then(({ data: res }) => { | |||
console.log("[DWM 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; | |||
} | |||
this.tableLoading = false; | |||
}); | |||
}, | |||
disableCondition(prop) { | |||
return this.detailMode || 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) => { | |||
console.log("reset form, key", key); | |||
if (excludeId && key === "id") return; | |||
if ("files" in this.dataForm) this.dataForm.files = []; | |||
else if ("fileIds" in this.dataForm) this.dataForm.fileIds = []; | |||
else this.$set(this.dataForm, key, null); | |||
if (Array.isArray(this.fileList)) { | |||
this.fileList = []; | |||
} | |||
}); | |||
this.activeMenu = this.configs.menu[0].name; | |||
this.$refs.dataForm[0].resetFields(); | |||
console.log("清除Form...", this.dataForm); | |||
}, | |||
immediate ? 0 : 200 | |||
); | |||
}, | |||
/** | |||
* 有些选项需要在确认后及时刷新 | |||
*/ | |||
async updateSelectOptions() { | |||
if (this.refreshQueue.length) { | |||
const promiseList = []; | |||
try { | |||
this.refreshQueue.forEach((opt) => | |||
promiseList.push(async function () { | |||
const { data: res } = await opt.fetchData(this.dataForm.id ? this.dataForm.id : -1); | |||
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 })) | |||
); | |||
} | |||
}) | |||
); | |||
await Promise.all(promiseList); | |||
return true; | |||
} catch (err) { | |||
this.$message.err("刷新选项失败"); | |||
return false; | |||
} | |||
} | |||
}, | |||
/** 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.formLoading = true; | |||
const newFilelist = this.fileList.filter((f) => f.id !== file.id); | |||
this.updateRemoteFiles(newFilelist).then((msg) => { | |||
if (msg && msg === "success") { | |||
this.fileList = newFilelist; | |||
this.formLoading = 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.formLoading = true; | |||
this.updateRemoteFiles([...this.fileList, fileItem]).then((msg) => { | |||
if (msg && msg === "success") { | |||
this.fileList.push(fileItem); | |||
this.formLoading = 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) {}, | |||
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.formLoading = 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.formLoading = false; | |||
console.log("[add&update] res is: ", res); | |||
if (res.code === 0) { | |||
this.$message.success(payload.name === "add" ? "添加成功" : "更新成功"); | |||
this.$emit("refreshDataList"); | |||
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.formLoading) this.formLoading = false; | |||
if (this.btnLoading) this.btnLoading = false; | |||
}); | |||
} else { | |||
this.$message.error("请核查字段信息"); | |||
} | |||
}); | |||
} | |||
} | |||
} | |||
}, | |||
handlePageChange(val) { | |||
this.getSubList(val, this.attrSize); | |||
}, | |||
handleSizeChange(val) { | |||
this.attrPage = 1; | |||
this.attrSize = val; | |||
this.getSubList(1, val); | |||
}, | |||
handleAddParam(id) { | |||
this.showSubDialog = true; | |||
this.$nextTick(() => { | |||
this.$refs.subDialog.init(id ?? null); | |||
}); | |||
}, | |||
handleClose() { | |||
// this.resetForm(); | |||
// this.$emit("update:dialogVisible", false); | |||
this.visible = 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> |
@@ -0,0 +1,523 @@ | |||
<!-- 表格页加上搜索条件 --> | |||
<template> | |||
<div class="list-view-with-head" ref="pointer-loading-ref"> | |||
<!-- <head-form :form-config="headFormConfig" @headBtnClick="btnClick" /> --> | |||
<BaseSearchForm :head-config="headConfig" @btn-click="handleBtnClick" /> | |||
<BaseListTable | |||
v-loading="tableLoading" | |||
:table-config="tableConfig.table" | |||
:column-config="tableConfig.column" | |||
:table-data="dataList" | |||
@operate-event="handleOperate" | |||
:current-page="page" | |||
:current-size="size" | |||
:refresh-layout-key="refreshLayoutKey" /> | |||
<el-pagination | |||
v-if="navigator" | |||
class="mt-5 flex justify-end" | |||
@size-change="handleSizeChange" | |||
@current-change="handlePageChange" | |||
:current-page.sync="page" | |||
:page-size.sync="size" | |||
:page-sizes="[10, 20, 50, 100]" | |||
:total="totalPage" | |||
layout="total, sizes, prev, pager, next, jumper"></el-pagination> | |||
<DialogWithMenu | |||
ref="edit-dialog" | |||
v-if="!!dialogConfigs && dialogType === DIALOG_WITH_MENU && dialogVisible" | |||
:configs="dialogConfigs" | |||
@refreshDataList="getList" | |||
@destroy="dialogVisible = false" /> | |||
<DialogJustForm | |||
ref="edit-dialog" | |||
v-if="!!dialogConfigs && dialogType === DIALOG_JUST_FORM" | |||
:dialog-visible.sync="dialogVisible" | |||
:configs="dialogConfigs" | |||
@refreshDataList="getList" | |||
@emit-data="handleOperate" /> | |||
</div> | |||
</template> | |||
<script> | |||
import BaseListTable from "@/components/BaseListTable.vue"; | |||
import BaseSearchForm from "@/components/BaseSearchForm.vue"; | |||
import DialogWithMenu from "./DialogWithMenu.vue"; | |||
import DialogJustForm from "@/components/DialogJustForm.vue"; | |||
import moment from "moment"; | |||
const DIALOG_WITH_MENU = "DialogWithMenu"; | |||
const DIALOG_JUST_FORM = "DialogJustForm"; | |||
const DIALOG_CARPAYLOAD = "DialogCarPayload"; | |||
export default { | |||
name: "ListViewWithHead", | |||
components: { | |||
BaseSearchForm, | |||
BaseListTable, | |||
DialogWithMenu, | |||
DialogJustForm, | |||
}, | |||
props: { | |||
navigator: { | |||
type: Boolean, | |||
default: true, | |||
}, | |||
tableConfig: { | |||
type: Object, | |||
default: () => ({ | |||
/** 列配置, 即 props **/ column: [], | |||
/** 表格整体配置 */ table: undefined, | |||
}), | |||
}, | |||
headConfig: { | |||
type: Object, | |||
default: () => ({}), | |||
}, | |||
/** 请求page接口的时候有些字段是必填的,没有会报500,把相关字段名传入这个prop: */ | |||
listQueryExtra: { | |||
type: Array, | |||
default: () => ["key"], | |||
}, | |||
attachListQueryExtra: { | |||
// 新增时,附带 listQueryExtra 里的哪些键和对应值 | |||
type: String, | |||
default: "", | |||
}, | |||
initDataWhenLoad: { type: Boolean, default: true }, | |||
/** dialog configs 或许可以从 tableConfig 计算出来 computed... */ | |||
dialogConfigs: { | |||
type: Object, | |||
default: () => null, | |||
}, | |||
triggerUpdate: { | |||
type: String, | |||
default: "", | |||
}, | |||
}, | |||
computed: { | |||
dialogType() { | |||
return this.dialogConfigs.menu ? DIALOG_WITH_MENU : DIALOG_JUST_FORM; | |||
}, | |||
}, | |||
activated() { | |||
this.refreshLayoutKey = this.layoutTable(); | |||
}, | |||
watch: { | |||
page: (val) => { | |||
console.log("page changed:", val); | |||
}, | |||
size: (val) => { | |||
console.log("size changed:", val); | |||
}, | |||
triggerUpdate(val, oldVal) { | |||
if (val && val !== oldVal) { | |||
// get list | |||
this.page = 1; | |||
this.size = "defaultPageSize" in this.tableConfig.column ? this.tableConfig.column.defaultPageSize : 20; | |||
this.getList(); | |||
} | |||
}, | |||
}, | |||
data() { | |||
return { | |||
DIALOG_WITH_MENU, | |||
DIALOG_JUST_FORM, | |||
DIALOG_CARPAYLOAD, | |||
dialogVisible: false, | |||
topBtnConfig: null, | |||
totalPage: 0, | |||
page: 1, | |||
size: 20, // 默认20 | |||
dataList: [], | |||
tableLoading: false, | |||
refreshLayoutKey: null, | |||
uploadDialogVisible: false, | |||
overlayVisible: false, | |||
cachedSearchCondition: {}, | |||
needAttachmentDialog: false, | |||
}; | |||
}, | |||
inject: ["urls"], | |||
mounted() { | |||
// 更新页面默认 size | |||
const size = "defaultPageSize" in this.tableConfig.column ? this.tableConfig.column.defaultPageSize : 20; | |||
this.size = size; | |||
this.initDataWhenLoad && this.getList(); | |||
}, | |||
methods: { | |||
/** 获取 列表数据 */ | |||
getList(queryParams) { | |||
this.tableLoading = true; | |||
const params = queryParams | |||
? { ...queryParams, page: this.page, limit: this.size } | |||
: { | |||
page: this.page, | |||
limit: this.size, | |||
}; | |||
if (!queryParams && this.listQueryExtra && this.listQueryExtra.length) { | |||
this.listQueryExtra.map((nameOrObj) => { | |||
if (typeof nameOrObj === "string") params[nameOrObj] = ""; | |||
else if (typeof nameOrObj === "object") { | |||
Object.keys(nameOrObj).forEach((key) => { | |||
params[key] = nameOrObj[key]; | |||
}); | |||
} | |||
}); | |||
this.cachedSearchCondition = Object.assign({}, params); | |||
} | |||
this.$http[this.urls.pageIsPostApi ? "post" : "get"]( | |||
this.urls.page, | |||
this.urls.pageIsPostApi | |||
? { | |||
...params, | |||
} | |||
: { | |||
params, | |||
} | |||
) | |||
.then(({ data: res }) => { | |||
console.log("[http response] res is: ", res); | |||
if (res.code === 0) { | |||
// page 场景: | |||
if ("list" in res.data) { | |||
// if (res.data.list.length == 0 && res.data.total != 0) { | |||
// // refresh list | |||
// if (this.page > 1) { | |||
// this.page -= 1 | |||
// this.getList() | |||
// return | |||
// } else return | |||
// } | |||
/** 破碎记录的特殊需求:数据要结合单位 material + materialUnitDictValue */ | |||
if ("attachDictValue" in this.tableConfig.column) { | |||
this.dataList = res.data.list.map((row) => { | |||
this.tableConfig.column.attachDictValue(row, "unit", "qty", "materialUnitDictValue"); | |||
return row; | |||
}); | |||
} else this.dataList = res.data.list; | |||
this.totalPage = res.data.total; | |||
} else if ("records" in res.data) { | |||
this.dataList = res.data.records.map((item) => ({ | |||
...item, | |||
id: item._id ?? item.id, | |||
})); | |||
this.totalPage = res.data.total; | |||
} else if (Array.isArray(res.data)) { | |||
this.dataList = res.data; | |||
} else { | |||
this.dataList.splice(0); | |||
this.totalPage = 0; | |||
} | |||
} else { | |||
this.$message({ | |||
message: `${res.code}: ${res.msg}`, | |||
type: "error", | |||
duration: 2000, | |||
}); | |||
} | |||
this.tableLoading = false; | |||
}) | |||
.catch((err) => { | |||
this.$message({ | |||
message: `${err}`, | |||
type: "error", | |||
duration: 2000, | |||
}); | |||
this.tableLoading = false; | |||
}); | |||
// } | |||
}, | |||
layoutTable() { | |||
return Math.random(); | |||
}, | |||
/** 处理 表格操作 */ | |||
handleOperate({ type, data }) { | |||
console.log("payload", type, data); | |||
// 编辑、删除、跳转路由、打开弹窗(动态component)都可以在配置里加上 url | |||
// payload: { type: string, data: string | number | object } | |||
switch (type) { | |||
case "delete": { | |||
// 找到删除的 prompt 字段 | |||
const deleteConfig = data.head?.options?.find((item) => item.name === "delete"); | |||
let promptName = data.name ?? data.id; | |||
if (deleteConfig && "promptField" in deleteConfig) { | |||
promptName = data[deleteConfig.promptField]; | |||
} | |||
let hintMsg = `确定要删除记录 "${promptName}" 吗?`; | |||
if (promptName == data.id) { | |||
// 如果 promptName 计算出来是 data.id 就以'该记录'代称 | |||
hintMsg = "确定删除该记录?"; | |||
} | |||
let currenPageListLength = this.dataList.length; | |||
// 确认是否删除 | |||
return this.$confirm(hintMsg, "提示", { | |||
confirmButtonText: "确认", | |||
cancelButtonText: "我再想想", | |||
type: "warning", | |||
}) | |||
.then(() => { | |||
// this.$http.delete(this.urls.base + `/${data}`).then((res) => { | |||
this.$http({ | |||
url: this.urls.base, | |||
method: "DELETE", | |||
// data: data.id, | |||
data: [`${data.id}`], | |||
// headers: { | |||
// "Content-Type": "application/json" | |||
// } | |||
}).then(({ data: res }) => { | |||
if (res.code === 0) { | |||
this.$message.success("删除成功!"); | |||
// this.page = 1; | |||
// this.size = | |||
// "defaultPageSize" in this.tableConfig.column ? this.tableConfig.column.defaultPageSize : 20; | |||
if (currenPageListLength == 1) this.page = this.page > 1 ? this.page - 1 : 1; | |||
this.getList(); | |||
} else { | |||
this.$message({ | |||
message: `${res.code}: ${res.msg}`, | |||
type: "error", | |||
duration: 1500, | |||
}); | |||
} | |||
}); | |||
}) | |||
.catch((err) => {}); | |||
} | |||
case "edit": { | |||
console.log("[edit] ", data); | |||
this.openDialog(data); /** data is ==> id */ | |||
break; | |||
} | |||
case "view": | |||
case "view-detail-action": { | |||
this.openDialog(data, true); | |||
break; | |||
} | |||
case "view-attachment": { | |||
this.openDialog(data, false, { key: "attachment" }); | |||
break; | |||
} | |||
case "copy": { | |||
let shouldShowOverlay = false; | |||
this.$confirm("是否复制该记录?", "提示", { | |||
confirmButtonText: "确定", | |||
cancelButtonText: "取消", | |||
type: "warning", | |||
}) | |||
.then(() => { | |||
// | |||
let payload = ""; | |||
console.log("copying...", type, data); | |||
if (typeof data === "object") { | |||
const head = data.head; | |||
const copyOpt = | |||
("options" in head && | |||
Array.isArray(head.options) && | |||
head.options.find((item) => item.name === "copy")) || | |||
null; | |||
if (copyOpt && "showOverlay" in copyOpt && copyOpt.showOverlay) { | |||
this.overlayVisible = true; | |||
shouldShowOverlay = true; | |||
payload = data.id; | |||
} | |||
} else payload = data; | |||
return this.$http.post(this.urls.copyUrl, payload, { | |||
headers: { | |||
"Content-Type": "application/json", | |||
}, | |||
}); | |||
}) | |||
.then(({ data: res }) => { | |||
if (res.code === 0) { | |||
this.$message({ | |||
message: "复制成功!", | |||
type: "success", | |||
duration: 1500, | |||
}); | |||
this.getList(); | |||
} else { | |||
this.$message({ | |||
message: `${res.code}: ${res.msg}`, | |||
type: "error", | |||
duration: 1500, | |||
}); | |||
} | |||
if (shouldShowOverlay) this.overlayVisible = false; | |||
}) | |||
.catch((errMsg) => { | |||
errMsg !== "cancel" && | |||
this.$message({ | |||
message: errMsg, | |||
type: "error", | |||
duration: 1500, | |||
}); | |||
if (shouldShowOverlay) this.overlayVisible = false; | |||
}); | |||
break; | |||
} | |||
case "sync": { | |||
let shouldShowOverlay = false; | |||
let payload = ""; | |||
console.log("sync...", type, data); | |||
if (typeof data === "object") { | |||
const head = data.head; | |||
const syncOpt = | |||
("options" in head && Array.isArray(head.options) && head.options.find((item) => item.name === "sync")) || | |||
null; | |||
if (syncOpt && "showOverlay" in syncOpt && syncOpt.showOverlay) { | |||
this.overlayVisible = true; | |||
shouldShowOverlay = true; | |||
payload = data.id; | |||
} | |||
} else payload = data; | |||
this.$message({ | |||
message: "正在发起同步...", | |||
type: "success", | |||
}); | |||
// 同步单个料仓数据 | |||
this.$http | |||
.post(this.urls.syncSingleUrl, payload, { | |||
headers: { | |||
"Content-Type": "application/json", | |||
}, | |||
}) | |||
.then(({ data: res }) => { | |||
this.$message({ | |||
message: res.msg, | |||
type: res.code === 0 ? "success" : "error", | |||
}); | |||
this.getList(); | |||
if (shouldShowOverlay) this.overlayVisible = false; | |||
}); | |||
} | |||
} | |||
}, | |||
handleBtnClick({ btnName, payload }) { | |||
console.log("[search] form handleBtnClick", btnName, payload); | |||
switch (btnName) { | |||
case "新增": | |||
this.openDialog(); | |||
break; | |||
case "导入": | |||
this.openUploadDialog(); | |||
break; | |||
case "手动添加": { | |||
this.openDialog(); | |||
return; | |||
} | |||
case "查询": { | |||
if (typeof payload === "object") { | |||
// BaseSearchForm 给这个组件传递了数据 | |||
Object.assign(this.cachedSearchCondition, payload); | |||
if ("timerange" in payload) { | |||
if (!!payload.timerange) { | |||
const [startTime, endTime] = payload["timerange"]; | |||
this.cachedSearchCondition.startTime = moment(startTime).format("YYYY-MM-DDTHH:mm:ss"); | |||
this.cachedSearchCondition.endTime = moment(endTime).format("YYYY-MM-DDTHH:mm:ss"); | |||
} else { | |||
delete this.cachedSearchCondition.startTime; | |||
delete this.cachedSearchCondition.endTime; | |||
} | |||
delete this.cachedSearchCondition.timerange; | |||
} | |||
} | |||
/** 处理 listQueryExtra 里的数据 */ | |||
this.listQueryExtra?.map((cond) => { | |||
if (typeof cond === "string") { | |||
if (!!payload[cond]) { | |||
this.cachedSearchCondition[cond] = payload[cond]; | |||
} else { | |||
this.cachedSearchCondition[cond] = ""; | |||
} | |||
} else if (typeof cond === "object") { | |||
Object.keys(cond).forEach((key) => { | |||
this.cachedSearchCondition[key] = cond[key]; | |||
}); | |||
} | |||
}); | |||
console.log("查询", this.cachedSearchCondition); | |||
this.getList(this.cachedSearchCondition); | |||
break; | |||
} | |||
case "同步": | |||
case "全部同步": | |||
this.$http.post(this.urls.syncUrl).then(({ data: res }) => { | |||
console.log("同步", res); | |||
this.$message({ message: res.msg, type: res.code === 0 ? "success" : "error" }); | |||
this.getList(); | |||
}); | |||
break; | |||
} | |||
}, | |||
/** 导航器的操作 */ | |||
handleSizeChange(val) { | |||
// val 是新值 | |||
this.page = 1; | |||
this.size = val; | |||
this.getList(this.cachedSearchCondition); | |||
}, | |||
handlePageChange(val) { | |||
// val 是新值 | |||
this.getList(this.cachedSearchCondition); | |||
}, | |||
/** 打开对话框 */ | |||
openDialog(row_id, detail_mode, tag_info) { | |||
this.dialogVisible = true; | |||
let extraParams = null; | |||
if (this.attachListQueryExtra && this.listQueryExtra.length) { | |||
this.listQueryExtra.forEach((item) => { | |||
let found = item[this.attachListQueryExtra]; | |||
if (found !== null && found !== undefined) extraParams = item; | |||
}); | |||
} | |||
this.$nextTick(() => { | |||
console.log(`[edit-dialog] extraParams: ${extraParams}`); | |||
this.$refs["edit-dialog"].init(/** some args... */ row_id, detail_mode, tag_info, extraParams); | |||
}); | |||
}, | |||
openUploadDialog() { | |||
this.uploadDialogVisible = true; | |||
this.$nextTick(() => { | |||
this.$refs["upload-dialog"].init(); | |||
}); | |||
}, | |||
}, | |||
}; | |||
</script> | |||
<style scoped> | |||
.list-view-with-head { | |||
background: white; | |||
/* height: 100%; */ | |||
min-height: inherit; | |||
border-radius: 6px; | |||
padding: 16px; | |||
box-shadow: 0 0 1.125px 0.125px rgba(0, 0, 0, 0.125); | |||
} | |||
</style> |
@@ -9,7 +9,8 @@ | |||
<script> | |||
import initConfig from "./config"; | |||
import ListViewWithHead from "@/views/atomViews/ListViewWithHead.vue"; | |||
// import ListViewWithHead from "@/views/atomViews/ListViewWithHead.vue"; | |||
import ListViewWithHead from "./components/ListViewWithHead.vue"; | |||
export default { | |||
name: "BlenderView", | |||
@@ -19,20 +20,6 @@ export default { | |||
urls: this.allUrls, | |||
}; | |||
}, | |||
// urls: { | |||
// type: Object, | |||
// required: true, | |||
// default: () => ({ | |||
// /** 列表 url **/ list: null, | |||
// /** 分页 url **/ page: null, | |||
// /** 编辑保存 url **/ edit: null, | |||
// /** 删除条目 url **/ delete: null, | |||
// /** 详情 url **/ detail: null, | |||
// /** 导出 url **/ export: null, | |||
// /** 导入 url **/ import: null, | |||
// /** 其他 url **/ other: null, | |||
// }), | |||
// }, | |||
data() { | |||
const { tableConfig, headFormConfigs, urls, dialogConfigs } = initConfig.call(this); | |||
return { | |||