diff --git a/src/api/production-master-data/factory-area.js b/src/api/production-master-data/factory-area.js
index 47f57eef..18fa5981 100644
--- a/src/api/production-master-data/factory-area.js
+++ b/src/api/production-master-data/factory-area.js
@@ -49,3 +49,11 @@ export function deleteFactoryArea (data) {
data: apiParams('delete', data)
})
}
+
+export function exportFactoryAreaTask (data) {
+ return request({
+ url: BASE + 'export',
+ method: 'post',
+ data: apiParams('export', data)
+ })
+}
diff --git a/src/api/production-master-data/production-line.js b/src/api/production-master-data/production-line.js
index 251bba3b..2c7acd82 100644
--- a/src/api/production-master-data/production-line.js
+++ b/src/api/production-master-data/production-line.js
@@ -41,3 +41,11 @@ export function deleteProductionLine (data) {
data: apiParams('delete', data)
})
}
+
+export function exportProductionLineTask (data) {
+ return request({
+ url: BASE + 'export',
+ method: 'post',
+ data: apiParams('export', data)
+ })
+}
diff --git a/src/locales/en.json b/src/locales/en.json
index 6c03947e..56309fc1 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -44,7 +44,20 @@
"tip": "Tip",
"confirm_delete": "Are you sure to delete?",
"validation_fail": "Validation failed",
- "please_enter": "Please enter {name}"
+ "please_enter": "Please enter {name}",
+ "parent_area": "Parent Area",
+ "select_parent_area": "Please select parent area",
+ "status": "Status",
+ "select_status": "Please select status",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "add_child": "Add Subarea",
+ "add_child_title": "Add Subarea",
+ "export": "Export",
+ "confirm_export": "Create export task?",
+ "length_1_100": "Length should be 1 to 100 characters",
+ "remark_max_500": "Remark cannot exceed 500 characters",
+ "parent_cycle_error": "Parent area cannot be itself or a child area"
},
"production_line": {
"search": "Search",
@@ -71,7 +84,15 @@
"confirm": "Confirm",
"tip": "Tip",
"confirm_delete": "Are you sure to delete?",
- "validation_fail": "Validation failed"
+ "validation_fail": "Validation failed",
+ "status": "Status",
+ "select_status": "Please select status",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "export": "Export",
+ "confirm_export": "Create export task?",
+ "length_1_100": "Length should be 1 to 100 characters",
+ "remark_max_500": "Remark cannot exceed 500 characters"
}
},
"process_model": {
diff --git a/src/locales/zh-chs.json b/src/locales/zh-chs.json
index 479b1c04..4378aae9 100644
--- a/src/locales/zh-chs.json
+++ b/src/locales/zh-chs.json
@@ -44,7 +44,20 @@
"tip": "提示",
"confirm_delete": "确定要执行该操作吗?",
"validation_fail": "校验失败",
- "please_enter": "请输入{name}"
+ "please_enter": "请输入{name}",
+ "parent_area": "上级区域",
+ "select_parent_area": "请选择上级区域",
+ "status": "状态",
+ "select_status": "请选择状态",
+ "enabled": "启用",
+ "disabled": "禁用",
+ "add_child": "新增子区域",
+ "add_child_title": "新增子区域",
+ "export": "导出",
+ "confirm_export": "确认创建导出任务?",
+ "length_1_100": "长度在 1 到 100 个字符",
+ "remark_max_500": "备注不能超过 500 个字符",
+ "parent_cycle_error": "上级区域不能选择自身或下级区域"
},
"production_line": {
"search": "查询",
@@ -71,7 +84,15 @@
"confirm": "确定",
"tip": "提示",
"confirm_delete": "确定要执行该操作吗?",
- "validation_fail": "校验失败"
+ "validation_fail": "校验失败",
+ "status": "状态",
+ "select_status": "请选择状态",
+ "enabled": "启用",
+ "disabled": "禁用",
+ "export": "导出",
+ "confirm_export": "确认创建导出任务?",
+ "length_1_100": "长度在 1 到 100 个字符",
+ "remark_max_500": "备注不能超过 500 个字符"
}
},
"process_model": {
diff --git a/src/views/production-master-data/factory-model/factory-area/index.vue b/src/views/production-master-data/factory-model/factory-area/index.vue
index ecd83472..b869181e 100644
--- a/src/views/production-master-data/factory-model/factory-area/index.vue
+++ b/src/views/production-master-data/factory-model/factory-area/index.vue
@@ -4,30 +4,20 @@
-
+
-
+
+
+
+
+
+
+
-
- {{ $t(key('search')) }}
-
-
- {{ $t(key('reset')) }}
-
+ {{ $t(key('search')) }}
+ {{ $t(key('reset')) }}
@@ -35,25 +25,31 @@
+ >
+
+ {{ $t(key('enabled')) }}
+ {{ $t(key('disabled')) }}
+
+
!disabledIds.has(String(this.getId(item))))
+ .map(item => ({ value: this.getId(item), label: item._treeLabel || item.name }))
+ }
+ },
created () {
this.columns = useTableColumns([
{ prop: 'sort', label: this.key('sort'), width: 80 },
- { prop: 'code', label: this.key('code'), minWidth: 120 },
- { prop: 'name', label: this.key('name'), minWidth: 120 },
+ { prop: 'code', label: this.key('code'), minWidth: 140 },
+ { prop: 'name', label: this.key('name'), minWidth: 160 },
+ { prop: 'status', label: this.key('status'), slot: 'status', width: 110 },
{ prop: 'remark', label: this.key('remark') },
- { prop: '_actions', label: this.key('operation'), width: 160, fixed: 'right' }
+ { prop: '_actions', label: this.key('operation'), width: 230, fixed: 'right' }
])
const btns = useTableButtons({
toolbar: [
- {
- key: 'add',
- label: this.key('add'),
- icon: 'el-icon-plus',
- type: 'primary',
- auth: '/production_configuration/factory_model/factory_area/create',
- onClick: this.openAdd
- }
+ { key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/production_configuration/factory_model/factory_area/create', onClick: this.openAdd },
+ { key: 'export', label: this.key('export'), icon: 'el-icon-download', auth: '/production_configuration/factory_model/factory_area/export', onClick: this.handleExport }
],
row: [
- {
- key: 'edit',
- label: this.key('edit'),
- icon: 'el-icon-edit',
- auth: '/production_configuration/factory_model/factory_area/edit',
- onClick: this.openEdit
- },
- {
- key: 'delete',
- label: this.key('delete'),
- icon: 'el-icon-delete',
- color: 'danger',
- auth: '/production_configuration/factory_model/factory_area/delete',
- onClick: this.handleDelete
- }
+ { key: 'addChild', label: this.key('add_child'), icon: 'el-icon-plus', auth: '/production_configuration/factory_model/factory_area/create', onClick: this.openAddChild },
+ { key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/production_configuration/factory_model/factory_area/edit', onClick: this.openEdit },
+ { key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/production_configuration/factory_model/factory_area/delete', onClick: this.handleDelete }
]
}, this.$permission)
this.toolbarButtons = btns.toolbarButtons
@@ -188,41 +161,89 @@ export default {
this.fetchData()
},
methods: {
+ getId (row) {
+ return row && (row.id !== undefined ? row.id : row.area_id)
+ },
+ getParentId (row) {
+ if (!row) return ''
+ if (row.parent_id !== undefined) return row.parent_id || ''
+ if (row.pid !== undefined) return row.pid || ''
+ if (row.parentId !== undefined) return row.parentId || ''
+ return ''
+ },
+ normalizeResponse (res) {
+ const data = res && res.data !== undefined ? res.data : res
+ if (Array.isArray(data)) return { list: data, total: data.length }
+ if (data && Array.isArray(data.list)) return { list: data.list, total: Number(data.count || data.total || data.list.length) }
+ if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
+ if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
+ return { list: [], total: 0 }
+ },
+ normalizeNode (row, level = 0) {
+ const children = Array.isArray(row.children) ? row.children.map(item => this.normalizeNode(item, level + 1)) : []
+ const node = { ...row, id: this.getId(row), parent_id: this.getParentId(row), children }
+ node._treeLabel = `${' '.repeat(level)}${node.name || ''}`
+ if (!node.children.length) delete node.children
+ return node
+ },
+ buildTree (list) {
+ if (list.some(item => Array.isArray(item.children))) return list.map(item => this.normalizeNode(item))
+ const map = new Map()
+ const roots = []
+ list.forEach(item => {
+ const node = this.normalizeNode(item)
+ node.children = []
+ map.set(String(node.id), node)
+ })
+ map.forEach(node => {
+ const parentId = this.getParentId(node)
+ const parent = parentId !== '' ? map.get(String(parentId)) : null
+ if (parent && String(parent.id) !== String(node.id)) parent.children.push(node)
+ else roots.push(node)
+ })
+ const mark = (nodes, level = 0) => nodes.map(node => {
+ node._treeLabel = `${' '.repeat(level)}${node.name || ''}`
+ if (node.children.length) node.children = mark(node.children, level + 1)
+ else delete node.children
+ return node
+ })
+ return mark(roots)
+ },
+ flattenTree (tree, result = []) {
+ tree.forEach(item => {
+ result.push(item)
+ if (Array.isArray(item.children)) this.flattenTree(item.children, result)
+ })
+ return result
+ },
+ collectDescendantIds (id) {
+ const current = this.flattenTree(this.tableData).find(item => String(this.getId(item)) === String(id))
+ if (!current || !Array.isArray(current.children)) return []
+ return this.flattenTree(current.children).map(item => String(this.getId(item)))
+ },
async fetchData () {
this.loading = true
try {
- const res = await getFactoryAreaList({
- ...this.search,
- page_no: this.pagination.current,
- page_size: this.pagination.size
- })
- const list = Array.isArray(res) ? res : (res.data || [])
- const total = Array.isArray(res) ? res.length : (res.count || 0)
- this.tableData = list
- this.pagination.total = total
+ const res = await getFactoryAreaList({ ...this.search, page_no: 1, page_size: 10000 })
+ const data = this.normalizeResponse(res)
+ this.rawAreaData = data.list
+ this.tableData = this.buildTree(data.list)
} finally {
this.loading = false
}
},
onSearch () {
- this.pagination.current = 1
this.fetchData()
},
onReset () {
- this.search = { code: '', name: '' }
- this.pagination.current = 1
- this.fetchData()
- },
- onPageChange (page) {
- this.pagination.current = page.current
- this.pagination.size = page.size
+ this.search = { code: '', name: '', status: '' }
this.fetchData()
},
onSelect (rows) {
this.selectedRows = rows
},
resetForm () {
- this.formData = { code: '', name: '', remark: '' }
+ this.formData = { code: '', name: '', parent_id: '', status: '1', remark: '' }
this.editId = ''
},
openAdd () {
@@ -234,25 +255,38 @@ export default {
this.dialogVisible = true
})
},
+ openAddChild (row) {
+ this.handleType = 'create'
+ this.dialogTitle = this.key('add_child_title')
+ this.$nextTick(() => {
+ this.$refs.dialogForm && this.$refs.dialogForm.reset()
+ this.resetForm()
+ this.formData.parent_id = this.getId(row)
+ this.dialogVisible = true
+ })
+ },
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
- this.editId = row.id
- this.formData = {
- code: row.code,
- name: row.name,
- remark: row.remark || ''
- }
+ this.editId = this.getId(row)
+ this.formData = { code: row.code, name: row.name, parent_id: this.getParentId(row), status: row.status !== undefined ? String(row.status) : '1', remark: row.remark || '' }
this.dialogVisible = true
},
+ validateHierarchy () {
+ if (this.handleType !== 'edit' || this.formData.parent_id === '') return true
+ const parentId = String(this.formData.parent_id)
+ if (parentId === String(this.editId) || this.collectDescendantIds(this.editId).includes(parentId)) {
+ this.$message.warning(this.$t(this.key('parent_cycle_error')))
+ return false
+ }
+ return true
+ },
async onDialogSubmit () {
+ if (!this.validateHierarchy()) return
this.submitting = true
try {
- if (this.handleType === 'create') {
- await createFactoryArea(this.formData)
- } else {
- await editFactoryArea({ ...this.formData, id: this.editId })
- }
+ if (this.handleType === 'create') await createFactoryArea(this.formData)
+ else await editFactoryArea({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
@@ -263,31 +297,32 @@ export default {
onDialogClose () {
this.resetForm()
},
+ async confirmAction (message, action) {
+ try {
+ await this.$confirm(this.$t(message), this.$t(this.key('tip')), { type: 'warning' })
+ } catch {
+ return false
+ }
+ await action()
+ return true
+ },
async handleDelete (row) {
- const cancelled = await this.$confirmAction(
- {
- message: this.key('confirm_delete'),
- title: this.key('tip')
- },
- () => deleteFactoryArea({ id: [row.id] })
- )
- if (cancelled) return
+ const ok = await this.confirmAction(this.key('confirm_delete'), () => deleteFactoryArea({ id: [this.getId(row)] }))
+ if (!ok) return
this.$message.success(this.$t(this.key('operation_success')))
- this.pagination.current = Math.min(
- this.pagination.current,
- Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
- )
this.fetchData()
+ },
+ async handleExport () {
+ const ok = await this.confirmAction(this.key('confirm_export'), () => exportFactoryAreaTask({ ...this.search }))
+ if (ok) this.$message.success(this.$t(this.key('operation_success')))
}
}
}
diff --git a/src/views/production-master-data/factory-model/production-line/index.vue b/src/views/production-master-data/factory-model/production-line/index.vue
index 583d76af..3dbb1d7c 100644
--- a/src/views/production-master-data/factory-model/production-line/index.vue
+++ b/src/views/production-master-data/factory-model/production-line/index.vue
@@ -28,6 +28,7 @@
clearable
filterable
style="width:200px"
+ @change="onSearch"
>
+
+
+
+
+
+
{{ $t(key('search')) }}
@@ -62,7 +75,12 @@
auto-height
@page-change="onPageChange"
@selection-change="onSelect"
- />
+ >
+
+ {{ $t(key('enabled')) }}
+ {{ $t(key('disabled')) }}
+
+
({ value: item.area_id || item.id, label: item.name }))
} catch { /* 忽略加载失败 */ }
},
async fetchData () {
this.loading = true
try {
- const res = await getProductionLineList({
- ...this.search,
- page_no: this.pagination.current,
- page_size: this.pagination.size
- })
- const list = Array.isArray(res) ? res : (res.data || [])
- const total = Array.isArray(res) ? res.length : (res.count || 0)
- this.tableData = list
- this.pagination.total = total
+ const res = await getProductionLineList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
+ const data = this.normalizeResponse(res)
+ this.tableData = data.list
+ this.pagination.total = data.total
} finally {
this.loading = false
}
@@ -261,7 +233,7 @@ export default {
this.fetchData()
},
onReset () {
- this.search = { code: '', name: '', area_id: '' }
+ this.search = { code: '', name: '', area_id: '', status: '' }
this.pagination.current = 1
this.fetchData()
},
@@ -274,7 +246,7 @@ export default {
this.selectedRows = rows
},
resetForm () {
- this.formData = { code: '', name: '', area_id: '', remark: '' }
+ this.formData = { code: '', name: '', area_id: '', status: '1', remark: '' }
this.editId = ''
},
openAdd () {
@@ -290,22 +262,14 @@ export default {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
this.editId = row.id
- this.formData = {
- code: row.code,
- name: row.name,
- area_id: row.area_id,
- remark: row.remark || ''
- }
+ this.formData = { code: row.code, name: row.name, area_id: row.area_id, status: row.status !== undefined ? String(row.status) : '1', remark: row.remark || '' }
this.dialogVisible = true
},
async onDialogSubmit () {
this.submitting = true
try {
- if (this.handleType === 'create') {
- await createProductionLine(this.formData)
- } else {
- await editProductionLine({ ...this.formData, id: this.editId })
- }
+ if (this.handleType === 'create') await createProductionLine(this.formData)
+ else await editProductionLine({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
@@ -316,31 +280,33 @@ export default {
onDialogClose () {
this.resetForm()
},
+ async confirmAction (message, action) {
+ try {
+ await this.$confirm(this.$t(message), this.$t(this.key('tip')), { type: 'warning' })
+ } catch {
+ return false
+ }
+ await action()
+ return true
+ },
async handleDelete (row) {
- const cancelled = await this.$confirmAction(
- {
- message: this.key('confirm_delete'),
- title: this.key('tip')
- },
- () => deleteProductionLine({ id: [row.id] })
- )
- if (cancelled) return
+ const ok = await this.confirmAction(this.key('confirm_delete'), () => deleteProductionLine({ id: [row.id] }))
+ if (!ok) return
this.$message.success(this.$t(this.key('operation_success')))
- this.pagination.current = Math.min(
- this.pagination.current,
- Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
- )
+ this.pagination.current = Math.min(this.pagination.current, Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1)
this.fetchData()
+ },
+ async handleExport () {
+ const ok = await this.confirmAction(this.key('confirm_export'), () => exportProductionLineTask({ ...this.search }))
+ if (ok) this.$message.success(this.$t(this.key('operation_success')))
}
}
}