weizong song 3 weeks ago
parent b32f066156
commit 237f6b8b03

@ -64,3 +64,54 @@ export function getBudgetCollectionYearOptions() {
method: 'get'
})
}
// 获取指定年度的经济分类
export function getYearEconomicClassifications(yearId) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications`,
method: 'get'
})
}
// 新增年度经济分类
export function createYearEconomicClassification(yearId, data) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications`,
method: 'post',
data
})
}
// 修改年度经济分类
export function updateYearEconomicClassification(yearId, classificationId, data) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications/${classificationId}`,
method: 'put',
data
})
}
// 删除未被使用的年度经济分类
export function deleteYearEconomicClassification(yearId, classificationId) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications/${classificationId}`,
method: 'delete'
})
}
// 保存年度经济分类排序
export function reorderYearEconomicClassifications(yearId, items) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications/order`,
method: 'put',
data: { items }
})
}
// 从最近一个更早年度复制经济分类
export function copyPreviousYearEconomicClassifications(yearId) {
return request({
url: `${BUDGET_API_PREFIX}/budget-collection-year/${yearId}/economic-classifications/copy-previous`,
method: 'post'
})
}

@ -46,10 +46,11 @@ export function updateBudgetDecomposition(id, data) {
}
// 获取经济分类列表
export function getEconomicCategories() {
export function getEconomicCategories(params) {
return request({
url: `${BUDGET_API_PREFIX}/economic-categories`,
method: 'get'
method: 'get',
params
})
}

@ -826,7 +826,12 @@ export default {
async loadEconomicCategories() {
try {
const response = await getEconomicCategories()
const yearId = this.packageData.year_id || (this.packageData.budget_year && this.packageData.budget_year.id)
if (!yearId) {
this.$message.error('预算包缺少预算年度,无法加载经济分类')
return
}
const response = await getEconomicCategories({ year_id: yearId })
if (response && !response.errcode) {
this.economicCategories = response || []
this.initEconomicCategoriesWithData()

@ -1034,7 +1034,12 @@ export default {
async loadEconomicCategories() {
try {
const response = await getEconomicCategories()
const yearId = this.mainPackage.year_id || (this.mainPackage.budget_year && this.mainPackage.budget_year.id)
if (!yearId) {
this.$message.error('预算包缺少预算年度,无法加载经济分类')
return
}
const response = await getEconomicCategories({ year_id: yearId })
if (response && !response.errcode) {
this.economicCategories = response || []
this.initEconomicCategoriesWithData()
@ -1926,4 +1931,4 @@ export default {
background-color: #f8f9fa;
border-radius: 4px;
}
</style>
</style>

@ -922,7 +922,12 @@ export default {
async loadEconomicCategories() {
try {
const response = await getEconomicCategories()
const yearId = this.packageData.year_id || (this.packageData.budget_year && this.packageData.budget_year.id)
if (!yearId) {
this.$message.error('预算包缺少预算年度,无法加载经济分类')
return
}
const response = await getEconomicCategories({ year_id: yearId })
if (response && !response.errcode) {
this.economicCategories = response || []
this.initEconomicCategoriesWithData()

@ -111,8 +111,11 @@
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="200">
<el-table-column fixed="right" label="操作" width="300">
<template slot-scope="scope">
<Button type="primary" @click="openEconomicClassifications(scope.row)" size="small" style="margin-right: 5px;" ghost>
经济分类
</Button>
<Button type="info" @click="edit(scope.row)" size="small" style="margin-right: 5px;" ghost>
编辑
</Button>
@ -232,6 +235,104 @@
</el-button>
</div>
</el-dialog>
<!-- 年度经济分类维护 -->
<el-dialog
:title="classificationDialogTitle"
:visible.sync="classificationDialogVisible"
width="760px"
append-to-body
>
<el-alert
v-if="!classificationEditable"
title="该预算年度已关闭,经济分类只能查看,不能修改。"
type="warning"
:closable="false"
show-icon
style="margin-bottom: 12px;"
/>
<div class="classification-toolbar">
<el-button type="primary" size="small" :disabled="!classificationEditable" @click="openClassificationForm()">
新增分类
</el-button>
<el-button
type="success"
size="small"
:disabled="!classificationEditable || classificationItems.length > 0"
@click="copyPreviousClassifications"
>
复制上一年度
</el-button>
<el-button
type="warning"
size="small"
:disabled="!classificationEditable || !classificationOrderDirty"
:loading="savingClassificationOrder"
@click="saveClassificationOrder"
>
保存排序
</el-button>
<span class="classification-count"> {{ classificationItems.length }} </span>
</div>
<el-table
v-loading="classificationLoading"
:data="classificationItems"
border
max-height="460"
empty-text="本年度暂无经济分类,可复制上一年度或手动新增"
>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="name" label="经济分类名称" min-width="260" />
<el-table-column prop="sort_order" label="排序" width="90" align="center" />
<el-table-column label="操作" width="250" align="center">
<template slot-scope="scope">
<el-button
type="text"
:disabled="!classificationEditable || scope.$index === 0"
@click="moveClassification(scope.$index, -1)"
>
上移
</el-button>
<el-button
type="text"
:disabled="!classificationEditable || scope.$index === classificationItems.length - 1"
@click="moveClassification(scope.$index, 1)"
>
下移
</el-button>
<el-button type="text" :disabled="!classificationEditable" @click="openClassificationForm(scope.row)">
编辑
</el-button>
<el-button type="text" class="danger-text-button" :disabled="!classificationEditable" @click="deleteClassification(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 新增/编辑经济分类 -->
<el-dialog
:title="classificationForm.id ? '编辑经济分类' : '新增经济分类'"
:visible.sync="classificationFormVisible"
width="460px"
append-to-body
>
<el-form ref="classificationForm" :model="classificationForm" :rules="classificationRules" label-width="100px">
<el-form-item label="分类名称" prop="name">
<el-input v-model.trim="classificationForm.name" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item label="排序序号" prop="sort_order">
<el-input-number v-model="classificationForm.sort_order" :min="1" :step="1" style="width: 100%;" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="classificationFormVisible = false">取消</el-button>
<el-button type="primary" :loading="savingClassification" @click="submitClassification"></el-button>
</div>
</el-dialog>
</div>
</template>
@ -239,11 +340,16 @@
import LxHeader from '@/components/LxHeader/index.vue'
import {
getBudgetCollectionYearList,
getBudgetCollectionYearDetail,
createBudgetCollectionYear,
updateBudgetCollectionYear,
deleteBudgetCollectionYear,
batchDeleteBudgetCollectionYear
batchDeleteBudgetCollectionYear,
getYearEconomicClassifications,
createYearEconomicClassification,
updateYearEconomicClassification,
deleteYearEconomicClassification,
reorderYearEconomicClassifications,
copyPreviousYearEconomicClassifications
} from '@/api/budget/budgetCollectionYear.js'
export default {
@ -257,6 +363,29 @@ export default {
formLabelWidth: '120px',
loading: false,
submitting: false,
classificationDialogVisible: false,
classificationLoading: false,
classificationEditable: false,
classificationYear: null,
classificationItems: [],
classificationOrderDirty: false,
savingClassificationOrder: false,
classificationFormVisible: false,
savingClassification: false,
classificationForm: {
id: '',
name: '',
sort_order: 1
},
classificationRules: {
name: [
{ required: true, message: '请输入经济分类名称', trigger: 'blur' },
{ max: 100, message: '经济分类名称不能超过100个字符', trigger: 'blur' }
],
sort_order: [
{ required: true, message: '请输入排序序号', trigger: 'change' }
]
},
selectedRows: [],
form: {
id: '',
@ -319,6 +448,11 @@ export default {
computed: {
dialogTitle() {
return this.form.id ? '编辑预算采集年份' : '新增预算采集年份'
},
classificationDialogTitle() {
return this.classificationYear
? `${this.classificationYear.year}年度经济分类`
: '年度经济分类'
}
},
created() {
@ -482,6 +616,147 @@ export default {
})
},
async openEconomicClassifications(row) {
this.classificationYear = { ...row }
this.classificationDialogVisible = true
await this.loadEconomicClassifications()
},
async loadEconomicClassifications() {
if (!this.classificationYear) return
this.classificationLoading = true
try {
const response = await getYearEconomicClassifications(this.classificationYear.id)
this.classificationItems = (response.items || []).map(item => ({ ...item }))
this.classificationEditable = response.editable === true
this.classificationOrderDirty = false
} catch (error) {
console.error(error)
this.classificationItems = []
} finally {
this.classificationLoading = false
}
},
openClassificationForm(row) {
if (!this.classificationEditable) return
this.classificationForm = row
? { id: row.id, name: row.name, sort_order: row.sort_order }
: {
id: '',
name: '',
sort_order: Math.max(0, ...this.classificationItems.map(item => Number(item.sort_order) || 0)) + 1
}
this.classificationFormVisible = true
this.$nextTick(() => this.$refs.classificationForm && this.$refs.classificationForm.clearValidate())
},
submitClassification() {
this.$refs.classificationForm.validate(async valid => {
if (!valid || !this.classificationYear) return
this.savingClassification = true
try {
const data = {
name: this.classificationForm.name,
sort_order: this.classificationForm.sort_order
}
if (this.classificationForm.id) {
await updateYearEconomicClassification(this.classificationYear.id, this.classificationForm.id, data)
this.$Message.success('经济分类修改成功')
} else {
await createYearEconomicClassification(this.classificationYear.id, data)
this.$Message.success('经济分类新增成功')
}
this.classificationFormVisible = false
await this.loadEconomicClassifications()
} catch (error) {
console.error(error)
} finally {
this.savingClassification = false
}
})
},
moveClassification(index, offset) {
const targetIndex = index + offset
if (targetIndex < 0 || targetIndex >= this.classificationItems.length) return
const items = [...this.classificationItems]
const moved = items.splice(index, 1)[0]
items.splice(targetIndex, 0, moved)
this.classificationItems = items.map((item, itemIndex) => ({
...item,
sort_order: itemIndex + 1
}))
this.classificationOrderDirty = true
},
async saveClassificationOrder() {
if (!this.classificationYear || !this.classificationOrderDirty) return
this.savingClassificationOrder = true
try {
const items = this.classificationItems.map(item => ({ id: item.id, sort_order: item.sort_order }))
await reorderYearEconomicClassifications(this.classificationYear.id, items)
this.$Message.success('排序保存成功')
await this.loadEconomicClassifications()
} catch (error) {
console.error(error)
} finally {
this.savingClassificationOrder = false
}
},
deleteClassification(row) {
if (!this.classificationYear) return
this.$confirm(
`确认删除经济分类“${row.name}”吗?已被预算填报使用的分类不能删除。`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}
).then(async() => {
try {
await deleteYearEconomicClassification(this.classificationYear.id, row.id)
this.$Message.success('删除成功')
await this.loadEconomicClassifications()
} catch (error) {
console.error(error)
}
}).catch(action => {
if (action !== 'cancel' && action !== 'close') {
console.error(action)
}
})
},
copyPreviousClassifications() {
if (!this.classificationYear) return
this.$confirm(
`将最近一个更早年度的全部经济分类复制到${this.classificationYear.year}年度。该操作仅允许在本年度分类为空时执行,是否继续?`,
'复制上一年度经济分类',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}
).then(async() => {
try {
const response = await copyPreviousYearEconomicClassifications(this.classificationYear.id)
this.$Message.success(`已从${response.source_year}年度复制${response.copied_count}项经济分类`)
await this.loadEconomicClassifications()
} catch (error) {
console.error(error)
}
}).catch(action => {
if (action !== 'cancel' && action !== 'close') {
console.error(action)
}
})
},
getDefaultForm() {
return {
id: '',
@ -538,6 +813,21 @@ export default {
padding: 12px 16px 16px 16px; /* 紧凑布局 */
}
.classification-toolbar {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.classification-count {
margin-left: auto;
color: #909399;
}
.danger-text-button {
color: #f56c6c;
}
.v-table {
border: 1px solid #e6e6e6;
}
@ -572,4 +862,4 @@ export default {
.el-tag {
margin-right: 5px;
}
</style>
</style>

Loading…
Cancel
Save