manage.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <template>
  2. <div class="page-container">
  3. <div class="page-header">
  4. <PageTitle title="分项费用类型明细" />
  5. <el-button type="danger" @click="handleBack">返回</el-button>
  6. </div>
  7. <el-table :data="itemDetailList" border>
  8. <el-table-column prop="deptName" label="部门名称" min-width="200" />
  9. <el-table-column prop="status" label="状态" min-width="100" align="center">
  10. <template #default="{ row }">
  11. <span :class="['status-text', row.status === '0' ? 'active' : '']">{{ row.status == '0' ? '启用' : '禁用' }}</span>
  12. </template>
  13. </el-table-column>
  14. <el-table-column prop="existingAmount" label="现有额度(年)" min-width="150" align="center">
  15. <template #default="{ row }">¥{{ (Number(row.existingAmount) || 0).toLocaleString() }}</template>
  16. </el-table-column>
  17. <el-table-column prop="usedQuota" label="已用额度(年)" min-width="150" align="center">
  18. <template #default="{ row }">¥{{ (Number(row.usedQuota) || 0).toLocaleString() }}</template>
  19. </el-table-column>
  20. <el-table-column prop="remainingAmount" label="剩余额度(年)" min-width="150" align="center">
  21. <template #default="{ row }">¥{{ (Number(row.remainingAmount) || 0).toLocaleString() }}</template>
  22. </el-table-column>
  23. <el-table-column label="操作" width="100" align="center">
  24. <template #default="{ row }">
  25. <el-button type="primary" link size="small" @click="handleRecharge(row)">充值额度</el-button>
  26. </template>
  27. </el-table-column>
  28. </el-table>
  29. <el-dialog v-model="rechargeVisible" title="充值额度" width="450px" @close="resetRechargeForm">
  30. <el-form :model="rechargeForm" label-width="110px">
  31. <el-form-item label="分项费用名称">
  32. <el-input :model-value="expenseName" disabled />
  33. </el-form-item>
  34. <el-form-item label="充值部门">
  35. <el-input :model-value="rechargeForm.deptName" disabled />
  36. </el-form-item>
  37. <el-form-item label="充值额度" required>
  38. <el-input v-model="rechargeForm.amount" placeholder="请输入充值金额" type="number" />
  39. </el-form-item>
  40. </el-form>
  41. <template #footer>
  42. <el-button @click="rechargeVisible = false">取消</el-button>
  43. <el-button type="primary" @click="handleRechargeSubmit" :loading="rechargeLoading">确定</el-button>
  44. </template>
  45. </el-dialog>
  46. </div>
  47. </template>
  48. <script setup lang="ts">
  49. import { ref, reactive, onMounted } from 'vue';
  50. import { useRouter, useRoute } from 'vue-router';
  51. import { ElMessage } from 'element-plus';
  52. import { expenseDetail, rechargeLimit } from '@/api/pc/cost/itemExpense';
  53. const router = useRouter();
  54. const route = useRoute();
  55. const itemDetailList = ref<any[]>([]);
  56. const expenseName = ref('');
  57. const rechargeVisible = ref(false);
  58. const rechargeLoading = ref(false);
  59. const rechargeForm = reactive({
  60. deptId: '',
  61. deptName: '',
  62. amount: ''
  63. });
  64. const handleRecharge = (row: any) => {
  65. expenseName.value = row.expenseName || '';
  66. rechargeForm.deptId = row.deptId || '';
  67. rechargeForm.deptName = row.deptName || '';
  68. rechargeForm.amount = '';
  69. rechargeVisible.value = true;
  70. };
  71. const resetRechargeForm = () => {
  72. rechargeForm.deptId = '';
  73. rechargeForm.deptName = '';
  74. rechargeForm.amount = '';
  75. };
  76. const handleRechargeSubmit = async () => {
  77. if (!rechargeForm.amount || Number(rechargeForm.amount) <= 0) {
  78. ElMessage.warning('请输入有效的充值金额');
  79. return;
  80. }
  81. rechargeLoading.value = true;
  82. try {
  83. const res = await rechargeLimit({
  84. deptId: rechargeForm.deptId,
  85. recharge: Number(rechargeForm.amount)
  86. });
  87. if (res.code === 200 || res.code === 0) {
  88. ElMessage.success('充值成功');
  89. rechargeVisible.value = false;
  90. loadDetail();
  91. } else {
  92. ElMessage.error(res.msg || '充值失败');
  93. }
  94. } catch (e) {
  95. console.error('充值失败:', e);
  96. ElMessage.error('充值失败');
  97. } finally {
  98. rechargeLoading.value = false;
  99. }
  100. };
  101. const loadDetail = async () => {
  102. const id = route.query.id as string;
  103. if (!id) return;
  104. try {
  105. const res = await expenseDetail(id);
  106. if (res.code === 200 || res.code === 0) {
  107. const data = res.data || res.rows || [];
  108. const list = Array.isArray(data) ? data : [data];
  109. itemDetailList.value = list.map((item: any) => {
  110. const existing = Number(item.existingAmount || item.creditAmount) || 0;
  111. const used = Number(item.usedQuota || item.usedAmount) || 0;
  112. return {
  113. id: item.id,
  114. deptId: item.deptId,
  115. deptName: item.deptName || '',
  116. expenseName: item.expenseName || '',
  117. status: item.status ?? '',
  118. existingAmount: existing,
  119. usedQuota: used,
  120. remainingAmount: existing - used
  121. };
  122. });
  123. }
  124. } catch (e) {
  125. console.error('查询费用明细失败:', e);
  126. }
  127. };
  128. const handleBack = () => {
  129. router.push({ path: '/cost/itemExpense' });
  130. };
  131. onMounted(() => {
  132. loadDetail();
  133. });
  134. </script>
  135. <style scoped lang="scss">
  136. .page-container {
  137. padding: 20px;
  138. background: #fff;
  139. min-height: 100%;
  140. flex: 1;
  141. }
  142. .page-header {
  143. display: flex;
  144. justify-content: space-between;
  145. align-items: center;
  146. margin-bottom: 20px;
  147. :deep(.page-title) {
  148. margin-bottom: 0;
  149. }
  150. }
  151. .page-title {
  152. font-size: 16px;
  153. font-weight: bold;
  154. display: flex;
  155. align-items: center;
  156. gap: 8px;
  157. margin-bottom: 20px;
  158. }
  159. .title-bar {
  160. display: inline-block;
  161. width: 3px;
  162. height: 16px;
  163. background: #e60012;
  164. border-radius: 2px;
  165. }
  166. .search-bar {
  167. display: flex;
  168. align-items: center;
  169. gap: 10px;
  170. margin-bottom: 20px;
  171. .search-right {
  172. flex: 1;
  173. display: flex;
  174. justify-content: flex-end;
  175. }
  176. }
  177. .pagination-wrap {
  178. display: flex;
  179. justify-content: space-between;
  180. align-items: center;
  181. margin-top: 20px;
  182. .total-text {
  183. font-size: 14px;
  184. color: #666;
  185. }
  186. }
  187. </style>