Compare commits

...

2 Commits

Author SHA1 Message Date
xjs ca70138ad7 fix: 对接接口 2026-08-13 12:31:29 +08:00
xjs 141f6708b8 fix: 修改待接待日期显示 2026-08-12 13:08:55 +08:00
10 changed files with 1544 additions and 4494 deletions

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,8 @@ export const getReceptionTaskPageUrl = () => `${baseUrl}/api/checkin/reception/p
export const getReceptionTaskClaimUrl = () => `${baseUrl}/api/checkin/reception/claim`;
/** 取消本人已领取或已分配的接待任务 */
export const getReceptionTaskCancelUrl = () => `${baseUrl}/api/checkin/reception/cancel`;
/** 完成接待任务 */
export const getReceptionTaskCompleteUrl = () => `${baseUrl}/api/checkin/reception/complete`;
/** 查询接待任务详情 */
export const getReceptionTaskDetailUrl = () => `${baseUrl}/api/checkin/reception/detail`;
/** 保存已完成未缴费任务的接待记录 */

View File

@ -5,6 +5,7 @@ import {
getReceptionSummaryUrl,
getReceptionTaskCancelUrl,
getReceptionTaskClaimUrl,
getReceptionTaskCompleteUrl,
getReceptionTaskDetailUrl,
getReceptionTaskPageUrl,
} from "./fetchUrl";
@ -61,6 +62,9 @@ export interface ReceptionTaskDTO {
receptionResult: string | null;
receptionRecord: string | null;
followSuggestion: string | null;
unpaidReason: string | null;
unpaidStatus: number | null;
unpaidStatusName: string | null;
nextFollowTime: string | null;
}
@ -112,6 +116,8 @@ export interface ReceptionDetailDTO {
receptionResult: string | null;
receptionRecord: string | null;
unpaidReason: string | null;
unpaidStatus: number | null;
unpaidStatusName: string | null;
followSuggestion: string | null;
nextFollowTime: string | null;
}
@ -124,6 +130,7 @@ export interface ReceptionDetailResult {
export interface SaveReceptionRecordParams {
taskId: number;
reason: string;
unpaidStatus: number;
}
export interface ReceptionStatisticsParams {
@ -193,6 +200,16 @@ export const cancelReceptionTask = async (taskId: number): Promise<ReceptionActi
};
};
/** 完成接待;请求字段按文档保持为 taskId。 */
export const completeReceptionTask = async (taskId: number, receptionResult: string = "已完成接待"): Promise<ReceptionActionResult> => {
const response = await postRequest(getReceptionTaskCompleteUrl(), { taskId, receptionResult }, { silent: true });
return {
success: response.code === 200,
message: response.code === 200 ? "" : getFailureMessage(response, "完成接待失败"),
};
};
/** 根据分页项 taskId 查询接待详情;接口 Query 参数名固定为 id。 */
export const fetchReceptionDetail = async (taskId: number): Promise<ReceptionDetailResult> => {
const response = await getRequest(getReceptionTaskDetailUrl(), { id: taskId }, { silent: true });

View File

@ -105,6 +105,16 @@ export function useReceptionData(pageSize = DEFAULT_PAGE_SIZE) {
queryVersion += 1;
});
const resetFilters = (): void => {
if (searchTimer) {
clearTimeout(searchTimer);
searchTimer = null;
}
keyword.value = "";
activeTab.value = "all";
reload();
};
return {
keyword,
activeTab,
@ -115,5 +125,6 @@ export function useReceptionData(pageSize = DEFAULT_PAGE_SIZE) {
showEmpty,
loadNextPage,
reload,
resetFilters,
};
}

View File

@ -3,6 +3,7 @@ import { showToast } from "vant";
import "vant/es/toast/style";
import {
cancelReceptionTask,
completeReceptionTask,
fetchReceptionSummary,
fetchReceptionTaskPage,
type ReceptionPaymentStatusCode,
@ -91,6 +92,7 @@ export function useReceptionMine() {
const loading = ref(false);
const error = ref(false);
const cancellingTaskId = ref<number | null>(null);
const completingTaskId = ref<number | null>(null);
const loadMine = async (): Promise<void> => {
if (loading.value) return;
@ -136,6 +138,26 @@ export function useReceptionMine() {
}
};
const completeTask = async (taskId: number): Promise<boolean> => {
if (completingTaskId.value !== null) return false;
completingTaskId.value = taskId;
try {
const result = await completeReceptionTask(taskId, "已完成接待");
if (!result.success) {
if (result.message) showToast(result.message);
return false;
}
ongoingTasks.value = ongoingTasks.value.filter((task) => task.taskId !== taskId);
await loadMine();
showToast("接待已完成");
return true;
} finally {
completingTaskId.value = null;
}
};
return {
summary,
overview,
@ -143,7 +165,9 @@ export function useReceptionMine() {
loading,
error,
cancellingTaskId,
completingTaskId,
loadMine,
cancelTask,
completeTask,
};
}

View File

@ -12,6 +12,7 @@ export function useReceptionRecord() {
const router = useRouter();
const detail = ref<ReceptionDetailDTO | null>(null);
const reason = ref("");
const unpaidStatus = ref<number | null>(null);
const loading = ref(false);
const saving = ref(false);
const error = ref(false);
@ -53,6 +54,7 @@ export function useReceptionRecord() {
detail.value = result.data;
reason.value = result.data.unpaidReason ?? "";
unpaidStatus.value = result.data.unpaidStatus ?? null;
};
const save = async (): Promise<void> => {
@ -77,9 +79,12 @@ export function useReceptionRecord() {
}
if (saving.value) return;
// 页面仅支持填写未缴费原因,跟进状态按接口要求提供默认值
const statusToSend = unpaidStatus.value ?? 1;
saving.value = true;
try {
const result = await saveReceptionRecord({ taskId: id, reason: normalizedReason });
const result = await saveReceptionRecord({ taskId: id, reason: normalizedReason, unpaidStatus: statusToSend });
if (!result.success) {
if (result.message) showToast(result.message);
return;
@ -87,6 +92,8 @@ export function useReceptionRecord() {
reason.value = normalizedReason;
detail.value.unpaidReason = normalizedReason;
detail.value.unpaidStatus = statusToSend;
unpaidStatus.value = statusToSend;
showToast("保存成功");
router.back();
} finally {

View File

@ -1,14 +1,52 @@
// 通用格式化 / 校验纯函数(无状态,供 composable 与页面复用)。
/** Date → `YYYY-MM-DD` */
export const formatDate = (date: Date | string): string => {
/** Date `YYYY-MM-DD`
*
* token
* - YYYY / MM / DD / HH / mm / ss
* - M / D / H / m / s
*
* @example
* formatDate(new Date()) // '2026-08-12'
* formatDate('2025-12-05', 'YYYY/MM/DD') // '2025/12/05'
* formatDate(date, 'YYYY-MM-DD HH:mm:ss') // '2026-08-12 09:05:30'
*/
export const formatDate = (
date: Date | string,
format: string = "YYYY-MM-DD"
): string => {
const transformedDate = typeof date === "string" ? new Date(date) : date;
if (Number.isNaN(transformedDate.getTime())) return "";
const year = transformedDate.getFullYear();
const month = String(transformedDate.getMonth() + 1).padStart(2, "0");
const day = String(transformedDate.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
const month = transformedDate.getMonth() + 1;
const day = transformedDate.getDate();
const hours = transformedDate.getHours();
const minutes = transformedDate.getMinutes();
const seconds = transformedDate.getSeconds();
const pad = (n: number) => String(n).padStart(2, "0");
// 按长度从长到短替换,避免短 token 干扰
const replacements: Array<[string, string]> = [
["YYYY", String(year)],
["MM", pad(month)],
["DD", pad(day)],
["HH", pad(hours)],
["mm", pad(minutes)],
["ss", pad(seconds)],
["M", String(month)],
["D", String(day)],
["H", String(hours)],
["m", String(minutes)],
["s", String(seconds)],
];
let result = format;
for (const [token, value] of replacements) {
result = result.split(token).join(value);
}
return result;
};
/** 手机号输入格式化:仅保留数字并截断到 11 位。 */

View File

@ -13,8 +13,10 @@
<VanList v-model:loading="loading" :finished="finished" :error="error"
:finished-text="records.length ? '没有更多了' : ''" error-text="加载失败,点击重试" @load="loadNextPage"
@update:error="(value: boolean) => (error = value)">
<VanEmpty v-if="showEmpty" image="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/inviteReception/icon_zanwu.png"
:image-size="[186, 144]" description="暂无接待数据" />
<VanEmpty v-if="showEmpty" image="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/inviteReception/icon_kong2.png"
:image-size="[136, 136]" description="暂无接待数据" >
<VanButton color="#1580FF" block class="" plain @click="resetFilters"></VanButton>
</VanEmpty>
<div v-else class="flex flex-col gap-12 px-15 py-15">
<article v-for="record in records" :key="record.taskId" class="rounded-8 bg-white p-15">
@ -60,7 +62,8 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import { Empty as VanEmpty, List as VanList, Search as VanSearch, Tab as VanTab, Tabs as VanTabs } from "vant";
import { Empty as VanEmpty, List as VanList, Search as VanSearch, Tab as VanTab, Tabs as VanTabs, Button as VanButton } from "vant";
import "vant/es/button/style";
import "vant/es/empty/style";
import "vant/es/list/style";
import "vant/es/search/style";
@ -85,7 +88,7 @@ const tabs: ReadonlyArray<{ label: string; value: ReceptionDataTab }> = [
];
const router = useRouter();
const { keyword, activeTab, records, loading, finished, error, showEmpty, loadNextPage } = useReceptionData();
const { keyword, activeTab, records, loading, finished, error, showEmpty, loadNextPage, resetFilters } = useReceptionData();
const formatCheckinTime = (value: string): string => {
const normalized = value.replace("T", " ");
@ -136,6 +139,12 @@ const openRecord = (record: ReceptionTaskDTO): void => {
}
:deep(.van-empty) {
--van-empty-padding: 50% 0;
--van-empty-padding: 20% 0;
}
:deep(.van-empty__bottom){
--van-empty-bottom-margin-top: 30px;
width: 260px;
--van-button-normal-font-size: 16px;
}
</style>

View File

@ -90,17 +90,16 @@
v-if="item.canCancel"
class="h-32 min-w-50 rounded-4 border border-solid border-[#1580ff] bg-white px-10 text-[3.25rem] leading-none text-[#1580ff] disabled:opacity-60"
type="button"
:disabled="cancellingTaskId !== null"
:disabled="cancellingTaskId === item.taskId || completingTaskId === item.taskId"
@click="openCancelDialog(item)">
{{ cancellingTaskId === item.taskId ? "取消中" : "取消" }}
</button>
<button
v-if="item.canComplete"
class="h-32 min-w-50 rounded-4 border-0 bg-[#1580ff] px-10 text-[3.25rem] leading-none text-white disabled:opacity-60"
class="h-32 min-w-50 rounded-4 border-0 bg-[#1580ff] px-10 text-[3.25rem] leading-none text-white disabled:bg-[#CCCCCC] disabled:text-white"
type="button"
:disabled="cancellingTaskId !== null"
:disabled="!item.canComplete || cancellingTaskId === item.taskId || completingTaskId === item.taskId"
@click="completeReception(item)">
完成
{{ completingTaskId === item.taskId ? "完成中" : "完成" }}
</button>
</div>
</div>
@ -120,8 +119,8 @@
overlay-class="reception-cancel-dialog__overlay"
show-cancel-button
:close-on-click-overlay="false"
:cancel-button-disabled="cancellingTaskId !== null"
:confirm-button-disabled="cancellingTaskId !== null"
:cancel-button-disabled="cancellingTaskId === pendingCancelTask?.taskId"
:confirm-button-disabled="cancellingTaskId === pendingCancelTask?.taskId"
:before-close="beforeCancelDialogClose" />
</main>
</template>
@ -138,7 +137,7 @@
import { useReceptionMine } from "@/composables/useReceptionMine";
const router = useRouter();
const { summary, overview, ongoingTasks, loading, error, cancellingTaskId, loadMine, cancelTask } = useReceptionMine();
const { summary, overview, ongoingTasks, loading, error, cancellingTaskId, completingTaskId, loadMine, cancelTask, completeTask } = useReceptionMine();
const showCancelDialog = ref(false);
const pendingCancelTask = ref<ReceptionTaskDTO | null>(null);
type CancelDialogAction = "confirm" | "cancel";
@ -163,10 +162,6 @@
router.push({ name: "reception-data" });
};
const completeReception = (item: ReceptionTaskDTO): void => {
void router.push({ name: "reception-record", params: { id: String(item.taskId) } });
};
const openCancelDialog = (item: ReceptionTaskDTO): void => {
pendingCancelTask.value = item;
showCancelDialog.value = true;
@ -185,6 +180,10 @@
if (success) pendingCancelTask.value = null;
return success;
};
const completeReception = (item: ReceptionTaskDTO): void => {
void completeTask(item.taskId);
};
</script>
<style scoped>

View File

@ -32,7 +32,7 @@
<div class="flex items-center gap-8">
<span class="min-w-0 truncate text-[4rem] font-600 leading-none text-[#000]">{{ item.studentName || "-" }}</span>
<span class="shrink-0 rounded-2 bg-[#E7F2FF] px-4 py-3 text-[2.5rem] leading-none text-[#1580FF]">
签到:{{ item.checkinTime }}
签到:{{ formatDate(item.checkinTime, "MM-DD HH:mm") }}
</span>
<span
v-if="activeTab === 'available' && item.waitingMinutes > 0"
@ -89,6 +89,7 @@
import "vant/es/tabs/style";
import type { ReceptionTaskDTO } from "@/api/reception";
import { useReceptionTasks, type ReceptionTaskTab } from "@/composables/useReceptionTasks";
import { formatDate } from "@/utils/format";
const tabs: ReadonlyArray<{ label: string; value: ReceptionTaskTab }> = [
{ label: "待接待", value: "available" },