优化项目管理
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:clearable="false"
|
||||
@change="loadStats"
|
||||
@change="onRangeChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -184,12 +184,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ref, reactive, onMounted, nextTick, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus, Search, Refresh } from "@element-plus/icons-vue";
|
||||
import {
|
||||
getContractList,
|
||||
getContractStats,
|
||||
getContractDetail,
|
||||
} from "@/api/crmContract";
|
||||
import ContractCreate from "./components/create.vue";
|
||||
import ContractDetail from "./components/detail.vue";
|
||||
@@ -207,6 +209,9 @@ import {
|
||||
formatMoney,
|
||||
} from "../dict";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
const createVisible = ref(false);
|
||||
@@ -223,28 +228,33 @@ const filters = reactive({
|
||||
});
|
||||
const pagination = reactive({ page: 1, pageSize: 20, total: 0 });
|
||||
|
||||
/** 统计周期:month/quarter/year/custom(自定义选区间) */
|
||||
const statsPeriod = ref("month");
|
||||
/** 统计周期:month/quarter/year/custom(自定义选区间),默认本年 */
|
||||
const statsPeriod = ref("year");
|
||||
const statsRange = ref([]);
|
||||
|
||||
/** 列表页统计:后端按周期汇总(排除已作废),周期参数透传给 Stats */
|
||||
const stats = ref({ total: 0, total_amount: 0, total_cost: 0, total_profit: 0 });
|
||||
|
||||
/** 周期参数:统计与列表共用,保证两者口径一致 */
|
||||
function periodParams() {
|
||||
const params = { period: statsPeriod.value };
|
||||
if (statsPeriod.value === "custom" && statsRange.value && statsRange.value.length === 2) {
|
||||
params.start_date = statsRange.value[0];
|
||||
params.end_date = statsRange.value[1];
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const params = { period: statsPeriod.value };
|
||||
if (statsPeriod.value === "custom" && statsRange.value && statsRange.value.length === 2) {
|
||||
params.start_date = statsRange.value[0];
|
||||
params.end_date = statsRange.value[1];
|
||||
}
|
||||
const res = await getContractStats(params);
|
||||
const res = await getContractStats(periodParams());
|
||||
stats.value = res?.data || stats.value;
|
||||
} catch {
|
||||
/* 统计失败不影响列表 */
|
||||
}
|
||||
}
|
||||
|
||||
/** 切到「自定义」首次未选区间时,默认当前月,避免卡片空值 */
|
||||
/** 周期变化:列表与统计一并刷新(回到第一页) */
|
||||
function onPeriodChange(val) {
|
||||
if (val === "custom" && (!statsRange.value || statsRange.value.length !== 2)) {
|
||||
const now = new Date();
|
||||
@@ -253,6 +263,15 @@ function onPeriodChange(val) {
|
||||
const last = new Date(y, now.getMonth() + 1, 0).getDate();
|
||||
statsRange.value = [`${y}-${m}-01`, `${y}-${m}-${last}`];
|
||||
}
|
||||
pagination.page = 1;
|
||||
fetchList();
|
||||
loadStats();
|
||||
}
|
||||
|
||||
/** 自定义区间变化:同样刷新列表与统计 */
|
||||
function onRangeChange() {
|
||||
pagination.page = 1;
|
||||
fetchList();
|
||||
loadStats();
|
||||
}
|
||||
|
||||
@@ -261,6 +280,16 @@ onMounted(() => {
|
||||
loadStats();
|
||||
});
|
||||
|
||||
// 页面被 keep-alive 缓存后 onMounted 只执行一次,改为监听 ?id 变化,
|
||||
// 保证每次从项目详情「合同管理」等入口带 id 跳转进来都能打开目标合同。
|
||||
watch(
|
||||
() => route.query.id,
|
||||
(id) => {
|
||||
if (id) openByQueryId();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -268,6 +297,8 @@ async function fetchList() {
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
...filters,
|
||||
// 与顶部统计同口径:按所选周期筛选列表
|
||||
...periodParams(),
|
||||
});
|
||||
tableData.value = res?.data?.list || [];
|
||||
pagination.total = res?.data?.total || 0;
|
||||
@@ -281,6 +312,30 @@ async function fetchList() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* URL 带 ?id= 时自动打开对应合同详情(供项目详情「合同管理」点击合同名称跳转)。
|
||||
* 打开后清除 query,避免刷新或返回本页时反复弹出。
|
||||
*/
|
||||
async function openByQueryId() {
|
||||
const id = route.query.id;
|
||||
if (!id) return;
|
||||
// 抽屉可能因 keep-alive 缓存仍处于打开状态,先收起再打开,确保按新合同重新加载
|
||||
detailVisible.value = false;
|
||||
await nextTick();
|
||||
try {
|
||||
const res = await getContractDetail(id);
|
||||
const row = res?.data;
|
||||
if (!row?.id) throw new Error("合同不存在或已被删除");
|
||||
openDetail(row);
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || "打开合同详情失败");
|
||||
} finally {
|
||||
const query = { ...route.query };
|
||||
delete query.id;
|
||||
router.replace({ path: route.path, query });
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1;
|
||||
fetchList();
|
||||
|
||||
Reference in New Issue
Block a user