From 2cfc4ce5ec5095aa7be712444c32de12ffa891e3 Mon Sep 17 00:00:00 2001
From: kongdeqiang <123456>
Date: 星期一, 23 三月 2026 11:40:55 +0800
Subject: [PATCH] fix: 更新系统
---
src/api/statistics.js | 10 +
src/api/unitTask.js | 39 +++
src/views/data/UnitTask.vue | 307 ++++++++++++++++++++++++++++++
src/views/statistics/UnitStatistics.vue | 194 +++++++++++++++++++
4 files changed, 550 insertions(+), 0 deletions(-)
diff --git a/src/api/statistics.js b/src/api/statistics.js
new file mode 100644
index 0000000..f7f483e
--- /dev/null
+++ b/src/api/statistics.js
@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+
+export const getUnitStatisticsPage = (params) => {
+ return request({
+ url: '/data-task/pageNew',
+ method: 'get',
+ params
+ })
+}
diff --git a/src/api/unitTask.js b/src/api/unitTask.js
new file mode 100644
index 0000000..55990fe
--- /dev/null
+++ b/src/api/unitTask.js
@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+export const getUnitTaskPage = (params) => {
+ return request({
+ url: '/data-task/page',
+ method: 'get',
+ params
+ })
+}
+
+export const getUnitTaskById = (id) => {
+ return request({
+ url: `/data-task/${id}`,
+ method: 'get'
+ })
+}
+
+export const createUnitTask = (data) => {
+ return request({
+ url: '/data-task',
+ method: 'post',
+ data
+ })
+}
+
+export const updateUnitTask = (data) => {
+ return request({
+ url: '/data-task',
+ method: 'put',
+ data
+ })
+}
+
+export const deleteUnitTask = (id) => {
+ return request({
+ url: `/data-task/${id}`,
+ method: 'delete'
+ })
+}
diff --git a/src/views/data/UnitTask.vue b/src/views/data/UnitTask.vue
new file mode 100644
index 0000000..3db51c6
--- /dev/null
+++ b/src/views/data/UnitTask.vue
@@ -0,0 +1,307 @@
+<template>
+ <div class="unit-task-management">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>鍗曚綅涓婁紶浠诲姟</span>
+ <el-button type="primary" :icon="Plus" @click="handleAdd">鏂板浠诲姟</el-button>
+ </div>
+ </template>
+
+ <el-form :inline="true" :model="searchForm" class="search-form">
+ <el-form-item label="鍗曚綅">
+ <el-select
+ v-model="searchForm.unitCode"
+ placeholder="璇烽�夋嫨鍗曚綅"
+ clearable
+ filterable
+ style="width: 250px"
+ >
+ <el-option
+ v-for="dept in flatDepartmentList"
+ :key="dept.deptCode"
+ :label="`${dept.deptCode} - ${dept.deptName}`"
+ :value="dept.deptCode"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" :icon="Search" @click="handleSearch">鏌ヨ</el-button>
+ <el-button :icon="Refresh" @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table :data="tableData" v-loading="loading" border stripe>
+ <el-table-column prop="unitCode" label="鍗曚綅缂栫爜" width="150" />
+ <el-table-column prop="unitName" label="鍗曚綅鍚嶇О" width="300" />
+ <el-table-column prop="taskCount" label="浠诲姟鏉℃暟" width="120" />
+ <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" width="300" />
+ <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" width="300" />
+ <el-table-column label="鎿嶄綔" fixed="right" >
+ <template #default="{ row }">
+ <el-button type="primary" link :icon="Edit" @click="handleEdit(row)">缂栬緫</el-button>
+ <el-button type="danger" link :icon="Delete" @click="handleDelete(row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <el-pagination
+ v-model:current-page="pagination.currentPage"
+ v-model:page-size="pagination.pageSize"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="pagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ style="margin-top: 20px; justify-content: flex-end"
+ />
+ </el-card>
+
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" @close="handleDialogClose">
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+ <el-form-item label="鍗曚綅" prop="unitCode">
+ <el-select
+ v-model="form.unitCode"
+ placeholder="璇烽�夋嫨鍗曚綅"
+ style="width: 100%"
+ filterable
+ @change="handleUnitChange"
+ :disabled="!!form.id"
+ >
+ <el-option
+ v-for="dept in flatDepartmentList"
+ :key="dept.deptCode"
+ :label="`${dept.deptCode} - ${dept.deptName}`"
+ :value="dept.deptCode"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍗曚綅鍚嶇О" prop="unitName">
+ <el-input v-model="form.unitName" placeholder="閫夋嫨鍗曚綅鍚庤嚜鍔ㄥ甫鍑�" disabled />
+ </el-form-item>
+ <el-form-item label="浠诲姟鏉℃暟" prop="taskCount">
+ <el-input-number v-model="form.taskCount" :min="0" style="width: 100%" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus, Edit, Delete,Refresh,Search } from '@element-plus/icons-vue'
+import { getDepartmentTree } from '@/api/department'
+import { getUnitTaskPage, createUnitTask, updateUnitTask, deleteUnitTask } from '@/api/unitTask'
+
+const loading = ref(false)
+const tableData = ref([])
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formRef = ref(null)
+const departmentList = ref([])
+
+const pagination = reactive({
+ currentPage: 1,
+ pageSize: 10,
+ total: 0
+})
+
+const form = reactive({
+ id: null,
+ unitCode: '',
+ unitName: '',
+ taskCount: 0
+})
+
+const searchForm = reactive({
+ unitName: '',
+ unitCode: ''
+})
+
+const rules = {
+ unitCode: [{ required: true, message: '璇烽�夋嫨鍗曚綅', trigger: 'change' }],
+ unitName: [{ required: true, message: '鍗曚綅鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+ taskCount: [{ required: true, message: '璇疯緭鍏ヤ换鍔℃潯鏁�', trigger: 'blur' }]
+}
+
+const flatDepartmentList = computed(() => {
+ const flatten = (list) => {
+ let result = []
+ list.forEach(item => {
+ result.push(item)
+ if (item.children && item.children.length > 0) {
+ result = result.concat(flatten(item.children))
+ }
+ })
+ return result
+ }
+ return flatten(departmentList.value)
+})
+
+const buildTree = (list) => {
+ const map = {}
+ const roots = []
+
+ list.forEach(item => {
+ map[item.deptCode] = { ...item, children: [] }
+ })
+
+ list.forEach(item => {
+ const parent = map[item.parentCode]
+ if (parent) {
+ parent.children.push(map[item.deptCode])
+ } else {
+ roots.push(map[item.deptCode])
+ }
+ })
+
+ const cleanEmptyChildren = (nodes) => {
+ nodes.forEach(node => {
+ if (node.children && node.children.length === 0) {
+ delete node.children
+ } else {
+ cleanEmptyChildren(node.children)
+ }
+ })
+ }
+
+ cleanEmptyChildren(roots)
+ return roots
+}
+
+const handleSearch = () => {
+ pagination.current = 1
+ fetchData()
+}
+
+const handleReset = () => {
+ searchForm.unitName = ''
+ handleSearch()
+}
+
+const fetchDepartmentList = async () => {
+ try {
+ const res = await getDepartmentTree()
+ departmentList.value = buildTree(res.data)
+ } catch (error) {
+ console.error('鑾峰彇閮ㄩ棬鍒楄〃澶辫触:', error)
+ }
+}
+
+const fetchData = async () => {
+ loading.value = true
+ try {
+ const res = await getUnitTaskPage({
+ page: pagination.currentPage,
+ size: pagination.pageSize,
+ ...searchForm
+ })
+ tableData.value = res.data.records || res.data
+ pagination.total = res.data.total || res.data.length
+ } catch (error) {
+ console.error('鑾峰彇鍗曚綅涓婁紶浠诲姟鍒楄〃澶辫触:', error)
+ } finally {
+ loading.value = false
+ }
+}
+
+const handleUnitChange = (value) => {
+ const dept = flatDepartmentList.value.find(item => item.deptCode === value)
+ if (dept) {
+ form.unitName = dept.deptName
+ }
+}
+
+const handleAdd = () => {
+ dialogTitle.value = '鏂板浠诲姟'
+ resetForm()
+ dialogVisible.value = true
+}
+
+const handleEdit = (row) => {
+ dialogTitle.value = '缂栬緫浠诲姟'
+ resetForm()
+ Object.assign(form, row)
+ dialogVisible.value = true
+}
+
+const handleDelete = async (row) => {
+ try {
+ await ElMessageBox.confirm('纭畾瑕佸垹闄よ浠诲姟鍚楋紵', '鎻愮ず', {
+ type: 'warning'
+ })
+ await deleteUnitTask(row.id)
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ fetchData()
+ } catch (error) {
+ if (error !== 'cancel') {
+ console.error('鍒犻櫎浠诲姟澶辫触:', error)
+ }
+ }
+}
+
+const handleSubmit = async () => {
+ if (!formRef.value) return
+
+ await formRef.value.validate(async (valid) => {
+ if (valid) {
+ try {
+ if (form.id) {
+ await updateUnitTask(form)
+ ElMessage.success('淇敼鎴愬姛')
+ } else {
+ await createUnitTask(form)
+ ElMessage.success('鏂板鎴愬姛')
+ }
+ dialogVisible.value = false
+ fetchData()
+ } catch (error) {
+ console.error('淇濆瓨浠诲姟澶辫触:', error)
+ }
+ }
+ })
+}
+
+const handleDialogClose = () => {
+ formRef.value?.resetFields()
+}
+
+const resetForm = () => {
+ form.id = null
+ form.unitCode = ''
+ form.unitName = ''
+ form.taskCount = 0
+}
+
+const handleSizeChange = (val) => {
+ pagination.pageSize = val
+ fetchData()
+}
+
+const handleCurrentChange = (val) => {
+ pagination.currentPage = val
+ fetchData()
+}
+
+onMounted(() => {
+ fetchDepartmentList()
+ fetchData()
+})
+</script>
+
+<style scoped>
+.unit-task-management {
+ padding: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+</style>
diff --git a/src/views/statistics/UnitStatistics.vue b/src/views/statistics/UnitStatistics.vue
new file mode 100644
index 0000000..343541b
--- /dev/null
+++ b/src/views/statistics/UnitStatistics.vue
@@ -0,0 +1,194 @@
+<template>
+ <div class="unit-statistics">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>鍚勫崟浣嶆暟鎹粺璁�</span>
+ </div>
+ </template>
+
+ <el-form :inline="true" :model="searchForm" class="search-form">
+ <el-form-item label="鍗曚綅">
+ <el-select
+ v-model="searchForm.unitCode"
+ placeholder="璇烽�夋嫨鍗曚綅"
+ clearable
+ filterable
+ style="width: 250px"
+ >
+ <el-option
+ v-for="dept in flatDepartmentList"
+ :key="dept.deptCode"
+ :label="`${dept.deptCode} - ${dept.deptName}`"
+ :value="dept.deptCode"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" :icon="Search" @click="handleSearch">鎼滅储</el-button>
+ <el-button :icon="Refresh" @click="handleReset">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <el-table :data="tableData" v-loading="loading" border stripe>
+ <el-table-column prop="unitCode" label="鍗曚綅缂栫爜" width="150" />
+ <el-table-column prop="unitName" label="鍗曚綅鍚嶇О" width="300" />
+ <el-table-column prop="taskCount" label="浠诲姟鏉℃暟" width="120" />
+ <el-table-column prop="successCount" label="瀹屾垚鏉℃暟" width="120" />
+ <el-table-column prop="status" label="瀹屾垚鐘舵��" width="100">
+ <template #default="{ row }">
+ <el-tag :type="row.status === 1 ? 'success' : 'warning'">
+ {{ row.status === 1 ? '宸插畬鎴�' : '鏈畬鎴�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="lastTime" label="鏈�鍚庝笂浼犳椂闂�" />
+ </el-table>
+
+ <el-pagination
+ v-model:current-page="pagination.currentPage"
+ v-model:page-size="pagination.pageSize"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="pagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ style="margin-top: 20px; justify-content: flex-end"
+ />
+ </el-card>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from 'vue'
+import { Search, Refresh } from '@element-plus/icons-vue'
+import { getDepartmentTree } from '@/api/department'
+import { getUnitStatisticsPage } from '@/api/statistics'
+
+const loading = ref(false)
+const tableData = ref([])
+const departmentList = ref([])
+
+const searchForm = reactive({
+ unitCode: ''
+})
+
+const pagination = reactive({
+ currentPage: 1,
+ pageSize: 10,
+ total: 0
+})
+
+const flatDepartmentList = computed(() => {
+ const flatten = (list) => {
+ let result = []
+ list.forEach(item => {
+ result.push(item)
+ if (item.children && item.children.length > 0) {
+ result = result.concat(flatten(item.children))
+ }
+ })
+ return result
+ }
+ return flatten(departmentList.value)
+})
+
+const buildTree = (list) => {
+ const map = {}
+ const roots = []
+
+ list.forEach(item => {
+ map[item.deptCode] = { ...item, children: [] }
+ })
+
+ list.forEach(item => {
+ const parent = map[item.parentCode]
+ if (parent) {
+ parent.children.push(map[item.deptCode])
+ } else {
+ roots.push(map[item.deptCode])
+ }
+ })
+
+ const cleanEmptyChildren = (nodes) => {
+ nodes.forEach(node => {
+ if (node.children && node.children.length === 0) {
+ delete node.children
+ } else {
+ cleanEmptyChildren(node.children)
+ }
+ })
+ }
+
+ cleanEmptyChildren(roots)
+ return roots
+}
+
+const fetchDepartmentList = async () => {
+ try {
+ const res = await getDepartmentTree()
+ departmentList.value = buildTree(res.data)
+ } catch (error) {
+ console.error('鑾峰彇閮ㄩ棬鍒楄〃澶辫触:', error)
+ }
+}
+
+const fetchData = async () => {
+ loading.value = true
+ try {
+ const res = await getUnitStatisticsPage({
+ page: pagination.currentPage,
+ size: pagination.pageSize,
+ unitCode: searchForm.unitCode
+ })
+ tableData.value = res.data.records || res.data
+ pagination.total = res.data.total || res.data.length
+ } catch (error) {
+ console.error('鑾峰彇缁熻鏁版嵁澶辫触:', error)
+ } finally {
+ loading.value = false
+ }
+}
+
+const handleSearch = () => {
+ pagination.currentPage = 1
+ fetchData()
+}
+
+const handleReset = () => {
+ searchForm.unitCode = ''
+ pagination.currentPage = 1
+ fetchData()
+}
+
+const handleSizeChange = (val) => {
+ pagination.pageSize = val
+ fetchData()
+}
+
+const handleCurrentChange = (val) => {
+ pagination.currentPage = val
+ fetchData()
+}
+
+onMounted(() => {
+ fetchDepartmentList()
+ fetchData()
+})
+</script>
+
+<style scoped>
+.unit-statistics {
+ padding: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.search-form {
+ margin-bottom: 20px;
+}
+</style>
--
Gitblit v1.9.1