20260719215738

This commit is contained in:
oneao committed 2026-07-19 21:57:40 +08:00
1 parent 35aa108a34
commit cda7f16d3a
116 files changed
+11826 -3669

No files matched your search

+47 -20
View File
@@ -2,7 +2,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { Ellipsis, ListFilter, Plus, Search } from "@lucide/vue";
import { loadDataApi } from "../services/api";
import { loadDataApi, pageDataApi } from "../services/api";
const route = useRoute();
const router = useRouter();
@@ -53,6 +53,9 @@ const advancedFilters = reactive(createEmptyAdvancedFilters());
const draftFilterCount = computed(
() => Object.values(advancedFilters).filter(isFilterValueFilled).length,
);
const hasActiveFilters = computed(
() => Boolean(filters.keyword.trim()) || appliedFilterCount.value > 0,
);
function createEmptyAdvancedFilters() {
return {
@@ -89,9 +92,22 @@ function getRowValue(row, candidates) {
function normalizeRow(row, index) {
const id = getRowValue(row, ["id", "b_id", "user_id", "userid"]);
const name = getRowValue(row, ["name", "b_name", "customer_name", "title"]);
const state = getRowValue(row, ["state", "b_state", "status"]);
const updatedAt = getRowValue(row, ["updated_at", "update_time", "updated_time", "b_updated_at"]);
const name = getRowValue(row, [
"name",
"b_name",
"customer_name",
"v_customer_company",
"b_ywno",
"title",
]);
const state = getRowValue(row, ["state", "b_state", "status", "b_canuse"]);
const updatedAt = getRowValue(row, [
"updated_at",
"b_updatedatetime",
"update_time",
"updated_time",
"b_updated_at",
]);
return {
...row,
@@ -176,7 +192,7 @@ function applyLocalFilters() {
function searchData() {
pagination.current = 1;
applyLocalFilters();
loadData();
}
async function loadData() {
@@ -188,11 +204,27 @@ async function loadData() {
loadError.value = "";
try {
const response = await loadDataApi(viewName);
const result = response.data;
const response = hasActiveFilters.value
? await loadDataApi(viewName)
: await pageDataApi({
viewName,
orderBy: String(route.meta.orderBy || "b_id DESC"),
pageNo: pagination.current,
pageSize: pagination.pageSize,
});
if (currentSequence !== loadSequence) return;
sourceRows.value = (Array.isArray(result) ? result : []).map(normalizeRow);
applyLocalFilters();
if (hasActiveFilters.value) {
sourceRows.value = (Array.isArray(response.data) ? response.data : []).map(normalizeRow);
applyLocalFilters();
} else {
const result = response.data ?? {};
sourceRows.value = [];
rows.value = (Array.isArray(result.rows) ? result.rows : []).map(normalizeRow);
total.value = Number(result.total) || 0;
pagination.current = Number(result.page_no) || pagination.current;
pagination.pageSize = Number(result.page_size) || pagination.pageSize;
}
} catch (error) {
if (currentSequence !== loadSequence) return;
sourceRows.value = [];
@@ -211,7 +243,11 @@ function openDetail(record) {
function changePage(page, pageSize) {
pagination.current = page;
pagination.pageSize = pageSize;
applyLocalFilters();
if (hasActiveFilters.value) {
applyLocalFilters();
} else {
loadData();
}
}
function resetAdvancedFilters() {
@@ -222,7 +258,7 @@ function applyAdvancedFilters() {
pagination.current = 1;
appliedFilterCount.value = draftFilterCount.value;
advancedOpen.value = false;
applyLocalFilters();
loadData();
}
function updateTableHeight() {
@@ -593,13 +629,4 @@ watch(
max-width: 100vw;
}
@media (max-width: 520px) {
.advanced-filter-form {
grid-template-columns: minmax(0, 1fr);
}
.filter-span-full {
grid-column: 1;
}
}
</style>
+80 -26
View File
@@ -1,33 +1,79 @@
<script setup>
import { computed, ref } from "vue";
import { computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ArrowLeft } from "@lucide/vue";
import { mockRecords } from "../data/mockRecords";
import { loadDataApi } from "../services/api";
const route = useRoute();
const router = useRouter();
const remark = ref("");
const loading = ref(false);
const loadError = ref("");
const sourceRecord = ref(null);
let loadSequence = 0;
const recordId = computed(() => String(route.params.id ?? ""));
const detailTitle = computed(() => route.meta.listTitle || route.meta.title || "详情");
const record = computed(
() =>
mockRecords.find((item) => item.id === recordId.value) ?? {
id: recordId.value,
name: `${detailTitle.value} ${recordId.value}`,
state: "草稿",
businessType: "未设置",
owner: "未设置",
customer: "未设置",
amount: "0.00",
currency: "CNY",
updated_at: "--",
},
);
function getRowValue(row, candidates) {
if (!row) return undefined;
const keys = Object.keys(row);
for (const candidate of candidates) {
const matchedKey = keys.find((key) => key.toLowerCase() === candidate.toLowerCase());
if (matchedKey !== undefined && row[matchedKey] != null) return row[matchedKey];
}
return undefined;
}
const record = computed(() => {
const row = sourceRecord.value;
return {
id: recordId.value,
name: getRowValue(row, ["name", "b_name", "v_customer_company", "b_ywno", "title"]) ?? "--",
state: getRowValue(row, ["state", "b_state", "status", "b_canuse"]) ?? "--",
businessType: getRowValue(row, ["business_type", "b_ywtype", "b_type"]) ?? "--",
owner: getRowValue(row, ["owner", "owner_name", "v_sales_name", "b_owner_id"]) ?? "--",
customer: getRowValue(row, ["customer", "customer_name", "v_customer_company"]) ?? "--",
amount: getRowValue(row, ["amount", "total_amount", "b_amount"]) ?? "--",
currency: getRowValue(row, ["currency", "currency_code", "b_currency"]) ?? "",
updated_at:
getRowValue(row, ["updated_at", "b_updatedatetime", "update_time", "updated_time"]) ?? "--",
};
});
function escapeSqlString(value) {
return String(value).replaceAll("'", "''");
}
async function loadDetail() {
const currentSequence = ++loadSequence;
const source = String(route.meta.source || "");
if (!source || !recordId.value) {
sourceRecord.value = null;
return;
}
loading.value = true;
loadError.value = "";
sourceRecord.value = null;
try {
const response = await loadDataApi(source, `b_id = N'${escapeSqlString(recordId.value)}'`);
if (currentSequence !== loadSequence) return;
sourceRecord.value = Array.isArray(response.data) ? (response.data[0] ?? null) : null;
if (!sourceRecord.value) loadError.value = "未找到对应记录";
} catch (error) {
if (currentSequence !== loadSequence) return;
loadError.value = error.message || "详情加载失败";
} finally {
if (currentSequence === loadSequence) loading.value = false;
}
}
function backToList() {
router.push(String(route.meta.listPath || "/dashboard"));
}
watch([() => route.meta.source, recordId], loadDetail, { immediate: true });
</script>
<template>
@@ -44,16 +90,24 @@ function backToList() {
</header>
<main class="detail-content">
<a-descriptions bordered size="small" :column="{ xs: 1, sm: 2, md: 3 }">
<a-descriptions-item label="编号">{{ recordId }}</a-descriptions-item>
<a-descriptions-item label="名称">{{ record.name }}</a-descriptions-item>
<a-descriptions-item label="状态">{{ record.state }}</a-descriptions-item>
<a-descriptions-item label="客户">{{ record.customer }}</a-descriptions-item>
<a-descriptions-item label="业务类型">{{ record.businessType }}</a-descriptions-item>
<a-descriptions-item label="负责人">{{ record.owner }}</a-descriptions-item>
<a-descriptions-item label="金额">{{ record.amount }} {{ record.currency }}</a-descriptions-item>
<a-descriptions-item label="更新时间">{{ record.updated_at }}</a-descriptions-item>
</a-descriptions>
<a-spin :spinning="loading">
<a-alert v-if="loadError" type="error" show-icon :message="loadError">
<template #action><a-button size="small" @click="loadDetail">重试</a-button></template>
</a-alert>
<a-descriptions v-else bordered size="small" :column="3">
<a-descriptions-item label="编号">{{ recordId }}</a-descriptions-item>
<a-descriptions-item label="名称">{{ record.name }}</a-descriptions-item>
<a-descriptions-item label="状态">{{ record.state }}</a-descriptions-item>
<a-descriptions-item label="客户">{{ record.customer }}</a-descriptions-item>
<a-descriptions-item label="业务类型">{{ record.businessType }}</a-descriptions-item>
<a-descriptions-item label="负责人">{{ record.owner }}</a-descriptions-item>
<a-descriptions-item label="金额">
{{ record.amount }} {{ record.currency }}
</a-descriptions-item>
<a-descriptions-item label="更新时间">{{ record.updated_at }}</a-descriptions-item>
</a-descriptions>
</a-spin>
<section class="detail-remark">
<div class="detail-remark__label">备注</div>
File diff suppressed because it is too large. Load diff