870 lines
23 KiB
Vue
870 lines
23 KiB
Vue
<template>
|
|
<div v-can="permissionCodes.customerFollowUpReminder.read" class="follow-up-reminder-list">
|
|
<div class="filter bg-white">
|
|
<t-input
|
|
v-model="customerSearchKeyword"
|
|
class="customer"
|
|
type="search"
|
|
borderless
|
|
placeholder="输入客户编码、姓名或拼音"
|
|
@enter="applyCustomerSearch"
|
|
@clear="clearCustomer"
|
|
>
|
|
<template #suffix-icon>
|
|
<t-button
|
|
size="extra-small"
|
|
theme="primary"
|
|
variant="outline"
|
|
content="选择客户"
|
|
@click.stop="customerSelectorVisible = true"
|
|
/>
|
|
</template>
|
|
</t-input>
|
|
<t-dropdown-menu ref="dropdownMenuRef" class="menus">
|
|
<t-dropdown-item
|
|
v-model="statusFilter"
|
|
:label="statusFilterLabel"
|
|
:options="statusOptions"
|
|
@change="handleStatusChange"
|
|
/>
|
|
<t-dropdown-item
|
|
v-model="dateFilterType"
|
|
:label="dateFilterLabel"
|
|
:options="dateOptions"
|
|
@change="handleDateFilterChange"
|
|
/>
|
|
<t-dropdown-item
|
|
:label="selectedResponsibleName"
|
|
:options="[]"
|
|
@open="openResponsibleSelector"
|
|
/>
|
|
</t-dropdown-menu>
|
|
</div>
|
|
<t-pull-down-refresh
|
|
class="list-wrap"
|
|
v-model="isRefreshing"
|
|
:refresh-timeout="30000"
|
|
@refresh="refresh"
|
|
@scrolltolower="fetch"
|
|
>
|
|
<t-cell-group class="summary" theme="card">
|
|
<t-cell title="提醒数量" :note="`共 ${pageResult.total} 条`" />
|
|
<t-cell v-if="dateFilterType !== 'all'" title="提醒时间" :note="dateFilterLabel" />
|
|
<t-cell v-if="selectedCustomer || selectedResponsibleId" title="筛选条件">
|
|
<template #note>
|
|
<div class="selected-filter">
|
|
<t-tag
|
|
v-if="selectedCustomer"
|
|
theme="primary"
|
|
variant="light-outline"
|
|
:content="`客户:${selectedCustomer.name || selectedCustomer.code || '未命名'}`"
|
|
/>
|
|
<t-tag
|
|
v-if="selectedResponsibleId"
|
|
theme="primary"
|
|
variant="light-outline"
|
|
:content="`负责人:${selectedResponsibleName}`"
|
|
/>
|
|
<t-button size="extra-small" variant="text" @click="clearFilters">清空筛选</t-button>
|
|
</div>
|
|
</template>
|
|
</t-cell>
|
|
</t-cell-group>
|
|
<t-cell-group v-if="pageResult.list.length" class="items fix-last-cell-line empty-cell-title-margin" theme="card">
|
|
<t-swipe-cell
|
|
v-for="(item, index) in pageResult.list"
|
|
:key="item.id || index"
|
|
class="item"
|
|
>
|
|
<t-cell
|
|
v-entrance-transition="index * 30"
|
|
class="cell"
|
|
arrow
|
|
@click="toDetail(item)"
|
|
>
|
|
<template #leftIcon>
|
|
<t-image
|
|
v-if="getCustomerPhotoURL(item.customer, 64)"
|
|
:src="getCustomerPhotoURL(item.customer, 64)"
|
|
:alt="getCustomerName(item)"
|
|
fit="cover"
|
|
class="avatar"
|
|
/>
|
|
<div v-else class="avatar fallback" v-text="getCustomerName(item).slice(0, 1)" />
|
|
</template>
|
|
<template #title>
|
|
<div class="title">
|
|
<div class="name-channel">
|
|
<span class="text-bold" v-text="getCustomerName(item)" />
|
|
<t-tag
|
|
v-if="item.notifyChannelList?.includes('INTERNAL')"
|
|
size="small"
|
|
theme="primary"
|
|
variant="outline"
|
|
content="站内信"
|
|
/>
|
|
<t-tag
|
|
v-if="item.notifyChannelList?.includes('SMS')"
|
|
size="small"
|
|
theme="warning"
|
|
variant="outline"
|
|
content="短信"
|
|
/>
|
|
</div>
|
|
<t-tag size="small" :theme="getStatusTheme(item.status)" :content="getStatusText(item.status)" />
|
|
</div>
|
|
</template>
|
|
<template #description>
|
|
<div class="description">
|
|
<div class="content clip-text" v-text="getContentSummary(item.content)" />
|
|
<div class="time-responsible">
|
|
<span
|
|
class="remind-time"
|
|
:class="getReminderTimeClass(item)"
|
|
v-text="getReminderTimeText(item.remindAt)"
|
|
/>
|
|
<span v-text="`负责人:${getResponsibleName(item)}`" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</t-cell>
|
|
<template #right>
|
|
<div class="swipe-actions">
|
|
<t-button
|
|
v-if="canComplete && item.status === 'PENDING'"
|
|
class="complete swipe-cell-button"
|
|
variant="text"
|
|
:loading="operatingId === item.id && operatingType === 'complete'"
|
|
@click.stop="confirmComplete(item)"
|
|
>完成</t-button>
|
|
<t-button
|
|
v-if="canDelete"
|
|
class="delete swipe-cell-button"
|
|
variant="text"
|
|
:loading="operatingId === item.id && operatingType === 'delete'"
|
|
@click.stop="confirmDelete(item)"
|
|
>删除</t-button>
|
|
</div>
|
|
</template>
|
|
</t-swipe-cell>
|
|
</t-cell-group>
|
|
<div class="center-tips" v-text="isLoading ? '正在加载提醒' : pageResult.list.length ? (isNoMore ? '已经到底了' : '上拉加载更多') : '暂无跟进提醒'" />
|
|
</t-pull-down-refresh>
|
|
<customer-selector
|
|
v-model="selectedCustomerId"
|
|
v-model:visible="customerSelectorVisible"
|
|
:initial-keyword="customerSearchKeyword"
|
|
:store-id="currentStoreStore.currentStoreId"
|
|
:enable-scan="true"
|
|
@change="handleCustomerSelected"
|
|
/>
|
|
<managed-user-selector
|
|
v-model="selectedResponsibleId"
|
|
v-model:visible="responsibleSelectorVisible"
|
|
:store-id="currentStoreStore.currentStoreId"
|
|
@change="handleResponsibleSelected"
|
|
/>
|
|
<t-popup v-model="startVisible" placement="bottom">
|
|
<t-date-time-picker
|
|
v-if="startVisible"
|
|
v-model="startDraft"
|
|
format="YYYY-MM-DD HH:mm"
|
|
:mode="['date', 'minute']"
|
|
title="提醒开始时间"
|
|
@cancel="startVisible = false"
|
|
@confirm="confirmStart"
|
|
/>
|
|
</t-popup>
|
|
<t-popup v-model="endVisible" placement="bottom">
|
|
<t-date-time-picker
|
|
v-if="endVisible"
|
|
v-model="endDraft"
|
|
format="YYYY-MM-DD HH:mm"
|
|
:mode="['date', 'minute']"
|
|
title="提醒结束时间"
|
|
@cancel="endVisible = false"
|
|
@confirm="confirmEnd"
|
|
/>
|
|
</t-popup>
|
|
</div>
|
|
<nav-bar-action-menu v-if="canCreate" :actions="actionList" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { DialogPlugin, type DropdownValue } from "tdesign-mobile-vue";
|
|
import { Time } from "timi-web";
|
|
import { type PageResult, useUserStore } from "timi-web-api";
|
|
import { Message } from "@/utils/message";
|
|
import { getErrorMessage } from "@/utils/error";
|
|
import FollowUpReminderAPI from "@/api/FollowUpReminderAPI";
|
|
import CustomerAPI from "@/api/CustomerAPI";
|
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
|
import ManagedUserSelector from "@/pages/followUpReminder/component/ManagedUserSelector.vue";
|
|
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
|
import { useNavBarStore } from "@/store/navBarStore";
|
|
import type { Customer } from "@/types/Customer";
|
|
import type { ManagedUser } from "@/types/ManagedUser";
|
|
import type {
|
|
FollowUpReminder,
|
|
FollowUpReminderPage,
|
|
FollowUpReminderStatus
|
|
} from "@/types/FollowUpReminder";
|
|
import { getCustomerPhotoURL } from "@/utils/customer";
|
|
import { parseDateTime } from "@/utils/date";
|
|
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
|
|
|
type DateFilterType = "all" | "today" | "next7" | "custom";
|
|
|
|
defineOptions({
|
|
name: "FollowUpReminderList"
|
|
});
|
|
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const userStore = useUserStore();
|
|
const currentStoreStore = useCurrentStoreStore();
|
|
const navBarStore = useNavBarStore();
|
|
|
|
const isGlobalManager = computed(() => userStore.hasRoles(gaoRoleCodes.globalManager));
|
|
const canCreate = computed(() => userStore.hasPermissions(permissionCodes.customerFollowUpReminder.create));
|
|
const canComplete = computed(() => userStore.hasPermissions(permissionCodes.customerFollowUpReminder.complete));
|
|
const canDelete = computed(() => userStore.hasPermissions(permissionCodes.customerFollowUpReminder.delete));
|
|
|
|
// ---------- 筛选 ----------
|
|
|
|
const statusFilter = ref<"" | FollowUpReminderStatus>("");
|
|
const statusOptions = [
|
|
{ label: "全部状态", value: "" },
|
|
{ label: "待完成", value: "PENDING" },
|
|
{ label: "已完成", value: "COMPLETED" }
|
|
];
|
|
const statusFilterLabel = computed(() => statusOptions.find(item => item.value === statusFilter.value)?.label || "全部状态");
|
|
const dateFilterType = ref<DateFilterType>("all");
|
|
const dateRange = ref<[string, string]>(["", ""]);
|
|
const dateOptions = [
|
|
{ label: "全部时间", value: "all" },
|
|
{ label: "今天", value: "today" },
|
|
{ label: "未来 7 天", value: "next7" },
|
|
{ label: "自定义范围", value: "custom" }
|
|
];
|
|
const dateFilterLabel = computed(() => {
|
|
if (dateFilterType.value === "custom" && dateRange.value[0]) {
|
|
return `${dateRange.value[0]} 至 ${dateRange.value[1]}`;
|
|
}
|
|
return dateOptions.find(item => item.value === dateFilterType.value)?.label || "全部时间";
|
|
});
|
|
const selectedCustomerId = ref("");
|
|
const selectedCustomer = ref<Customer>();
|
|
const customerSearchKeyword = ref("");
|
|
const customerSelectorVisible = ref(false);
|
|
const selectedResponsibleId = ref("");
|
|
const selectedResponsible = ref<ManagedUser>();
|
|
const responsibleSelectorVisible = ref(false);
|
|
const dropdownMenuRef = ref<{ collapseMenu: () => void }>();
|
|
const selectedResponsibleName = computed(() => selectedResponsible.value
|
|
? getManagedUserName(selectedResponsible.value)
|
|
: "全部负责人");
|
|
const startVisible = ref(false);
|
|
const endVisible = ref(false);
|
|
const startDraft = ref("");
|
|
const endDraft = ref("");
|
|
|
|
const actionList = computed(() => [{
|
|
action: toCreate,
|
|
icon: "add",
|
|
key: "create",
|
|
text: "新增跟进提醒",
|
|
theme: "primary" as const
|
|
}]);
|
|
|
|
async function handleStatusChange(value: DropdownValue): Promise<void> {
|
|
if (Array.isArray(value)) {
|
|
return;
|
|
}
|
|
statusFilter.value = String(value) as "" | FollowUpReminderStatus;
|
|
await refresh();
|
|
}
|
|
|
|
async function handleDateFilterChange(value: DropdownValue): Promise<void> {
|
|
if (Array.isArray(value)) {
|
|
return;
|
|
}
|
|
dateFilterType.value = String(value) as DateFilterType;
|
|
if (dateFilterType.value === "custom") {
|
|
openStartDate();
|
|
return;
|
|
}
|
|
dateRange.value = buildPresetDateRange(dateFilterType.value);
|
|
await refresh();
|
|
}
|
|
|
|
function openStartDate(): void {
|
|
startDraft.value = dateRange.value[0] || `${Time.formatDate(new Date())} 00:00`;
|
|
startVisible.value = true;
|
|
}
|
|
|
|
function openEndDate(): void {
|
|
endDraft.value = dateRange.value[1] || `${Time.formatDate(new Date())} 23:59`;
|
|
endVisible.value = true;
|
|
}
|
|
|
|
function confirmStart(value?: string | number): void {
|
|
startVisible.value = false;
|
|
dateRange.value = [String(value || startDraft.value), dateRange.value[1]];
|
|
openEndDate();
|
|
}
|
|
|
|
async function confirmEnd(value?: string | number): Promise<void> {
|
|
endVisible.value = false;
|
|
dateRange.value = [dateRange.value[0], String(value || endDraft.value)];
|
|
if (dateRange.value[1] < dateRange.value[0]) {
|
|
dateRange.value = [dateRange.value[1], dateRange.value[0]];
|
|
}
|
|
await refresh();
|
|
}
|
|
|
|
function buildPresetDateRange(type: DateFilterType): [string, string] {
|
|
const begin = new Date();
|
|
begin.setHours(0, 0, 0, 0);
|
|
const end = new Date(begin);
|
|
if (type === "next7") {
|
|
end.setDate(end.getDate() + 6);
|
|
}
|
|
end.setHours(23, 59, 0, 0);
|
|
return type === "all" ? ["", ""] : [Time.formatDateTime(begin), Time.formatDateTime(end)];
|
|
}
|
|
|
|
async function handleCustomerSelected(id: string): Promise<void> {
|
|
selectedCustomerId.value = id;
|
|
selectedCustomer.value = id ? await loadCustomer(id) : undefined;
|
|
customerSearchKeyword.value = getCustomerSearchKeyword(selectedCustomer.value);
|
|
await refresh();
|
|
}
|
|
|
|
async function applyCustomerSearch(): Promise<void> {
|
|
const keyword = customerSearchKeyword.value.trim();
|
|
if (!keyword) {
|
|
await clearCustomer();
|
|
return;
|
|
}
|
|
if (keyword === getCustomerSearchKeyword(selectedCustomer.value)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await CustomerAPI.list({
|
|
index: 0,
|
|
size: 20,
|
|
storeId: currentStoreStore.currentStoreId || undefined,
|
|
keyword
|
|
});
|
|
const customer = result.list?.[0];
|
|
if (!customer?.id) {
|
|
Message.warning("未找到匹配客户");
|
|
return;
|
|
}
|
|
if (1 < (result.total || 0)) {
|
|
Message.warning("匹配到多个客户,请点击选择客户");
|
|
customerSelectorVisible.value = true;
|
|
return;
|
|
}
|
|
selectedCustomerId.value = customer.id;
|
|
selectedCustomer.value = customer;
|
|
customerSearchKeyword.value = getCustomerSearchKeyword(customer);
|
|
await refresh();
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "客户信息加载失败"));
|
|
}
|
|
}
|
|
|
|
function getCustomerSearchKeyword(customer?: Customer): string {
|
|
return customer?.name?.trim() || customer?.code?.trim() || "";
|
|
}
|
|
|
|
async function clearCustomer(): Promise<void> {
|
|
selectedCustomerId.value = "";
|
|
selectedCustomer.value = undefined;
|
|
customerSearchKeyword.value = "";
|
|
await refresh();
|
|
}
|
|
|
|
async function handleResponsibleSelected(item: ManagedUser): Promise<void> {
|
|
selectedResponsible.value = item;
|
|
await refresh();
|
|
}
|
|
|
|
function openResponsibleSelector(): void {
|
|
dropdownMenuRef.value?.collapseMenu();
|
|
responsibleSelectorVisible.value = true;
|
|
}
|
|
|
|
async function clearFilters(): Promise<void> {
|
|
statusFilter.value = "";
|
|
dateFilterType.value = "all";
|
|
dateRange.value = ["", ""];
|
|
selectedCustomerId.value = "";
|
|
selectedCustomer.value = undefined;
|
|
customerSearchKeyword.value = "";
|
|
selectedResponsibleId.value = "";
|
|
selectedResponsible.value = undefined;
|
|
await refresh();
|
|
}
|
|
|
|
// ---------- 列表 ----------
|
|
|
|
const isRefreshing = ref(false);
|
|
const isLoading = ref(false);
|
|
const isNoMore = ref(false);
|
|
const pageResult = reactive<PageResult<FollowUpReminder>>({
|
|
total: 0,
|
|
list: []
|
|
});
|
|
const page = reactive<FollowUpReminderPage>({
|
|
index: 0,
|
|
size: 16
|
|
});
|
|
let fetchTask: Promise<void> | undefined;
|
|
|
|
async function fetch(): Promise<void> {
|
|
if (isNoMore.value) {
|
|
return;
|
|
}
|
|
if (fetchTask) {
|
|
await fetchTask;
|
|
return;
|
|
}
|
|
|
|
const task = (async () => {
|
|
isLoading.value = true;
|
|
try {
|
|
const [beginRemindAt, endRemindAt] = buildDateTimestampRange();
|
|
const result = await FollowUpReminderAPI.list({
|
|
...page,
|
|
storeId: currentStoreStore.currentStoreId || undefined,
|
|
customerId: selectedCustomerId.value || undefined,
|
|
responsibleUserId: selectedResponsibleId.value || undefined,
|
|
status: statusFilter.value || undefined,
|
|
beginRemindAt,
|
|
endRemindAt
|
|
});
|
|
const list = result.list || [];
|
|
pageResult.total = result.total || 0;
|
|
pageResult.list = pageResult.list.concat(list);
|
|
page.index += 1;
|
|
isNoMore.value = list.length < page.size || pageResult.list.length >= pageResult.total;
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
})();
|
|
fetchTask = task;
|
|
try {
|
|
await task;
|
|
} finally {
|
|
if (fetchTask === task) {
|
|
fetchTask = undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
async function refresh(): Promise<void> {
|
|
if (fetchTask) {
|
|
await fetchTask;
|
|
}
|
|
isRefreshing.value = true;
|
|
try {
|
|
page.index = 0;
|
|
pageResult.total = 0;
|
|
pageResult.list = [];
|
|
isNoMore.value = false;
|
|
await fetch();
|
|
} finally {
|
|
isRefreshing.value = false;
|
|
}
|
|
}
|
|
|
|
function buildDateTimestampRange(): [number | undefined, number | undefined] {
|
|
return [
|
|
dateRange.value[0] ? Time.toTimestamp(parseDateTime(dateRange.value[0], false)) : undefined,
|
|
dateRange.value[1] ? Time.toTimestamp(parseDateTime(dateRange.value[1], true)) : undefined
|
|
];
|
|
}
|
|
|
|
async function loadCustomer(id: string): Promise<Customer | undefined> {
|
|
try {
|
|
return await CustomerAPI.detail(id);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
async function loadStoreScope(): Promise<void> {
|
|
if (isGlobalManager.value) {
|
|
await currentStoreStore.loadStoreList();
|
|
return;
|
|
}
|
|
await currentStoreStore.loadLoginStore();
|
|
}
|
|
|
|
// ---------- 展示 ----------
|
|
|
|
function getCustomerName(item: FollowUpReminder): string {
|
|
return item.customer?.name || item.customer?.code || item.customerId || "未命名客户";
|
|
}
|
|
|
|
function getManagedUserName(item: ManagedUser): string {
|
|
return item.user?.nick || item.user?.name || item.userId || "未命名负责人";
|
|
}
|
|
|
|
function getResponsibleName(item: FollowUpReminder): string {
|
|
return item.responsibleUser?.nick || item.responsibleUser?.name || item.responsibleUserId || "未命名负责人";
|
|
}
|
|
|
|
function getReminderTimeText(remindAt?: number): string {
|
|
return remindAt ? Time.toDateTime(remindAt) : "-";
|
|
}
|
|
|
|
function getReminderTimeClass(item: FollowUpReminder): "near" | "soon" | "later" | "normal" {
|
|
if (item.status === "COMPLETED" || !item.remindAt) {
|
|
return "normal";
|
|
}
|
|
const distance = item.remindAt - Time.now();
|
|
if (distance < 24 * 60 * 60 * 1000) {
|
|
return "near";
|
|
}
|
|
if (distance < 3 * 24 * 60 * 60 * 1000) {
|
|
return "soon";
|
|
}
|
|
return "later";
|
|
}
|
|
|
|
function getContentSummary(content?: string): string {
|
|
const text = content?.trim() || "未填写跟进内容";
|
|
return 60 < text.length ? `${text.slice(0, 60)}...` : text;
|
|
}
|
|
|
|
function getStatusText(status?: FollowUpReminderStatus): string {
|
|
return status === "COMPLETED" ? "已完成" : "待完成";
|
|
}
|
|
|
|
function getStatusTheme(status?: FollowUpReminderStatus): "success" | "warning" {
|
|
return status === "COMPLETED" ? "success" : "warning";
|
|
}
|
|
|
|
// ---------- 操作 ----------
|
|
|
|
const operatingId = ref("");
|
|
const operatingType = ref<"complete" | "delete">();
|
|
|
|
function confirmComplete(item: FollowUpReminder): void {
|
|
if (!item.id || !canComplete.value || item.status !== "PENDING" || operatingId.value) {
|
|
return;
|
|
}
|
|
const dialog = DialogPlugin.confirm({
|
|
title: "完成跟进提醒",
|
|
content: "完成后不能重新打开,确定完成此跟进提醒吗?",
|
|
cancelBtn: "取消",
|
|
confirmBtn: "完成",
|
|
onCancel() {
|
|
dialog.destroy();
|
|
},
|
|
onConfirm: async () => {
|
|
dialog.destroy();
|
|
await complete(item.id || "");
|
|
}
|
|
});
|
|
}
|
|
|
|
async function complete(id: string): Promise<void> {
|
|
operatingId.value = id;
|
|
operatingType.value = "complete";
|
|
try {
|
|
await FollowUpReminderAPI.complete(id);
|
|
const item = pageResult.list.find(current => current.id === id);
|
|
if (item) {
|
|
item.status = "COMPLETED";
|
|
}
|
|
Message.success("跟进提醒已完成");
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "跟进提醒完成失败"));
|
|
} finally {
|
|
operatingId.value = "";
|
|
operatingType.value = undefined;
|
|
}
|
|
}
|
|
|
|
function confirmDelete(item: FollowUpReminder): void {
|
|
if (!item.id || !canDelete.value || operatingId.value) {
|
|
return;
|
|
}
|
|
const dialog = DialogPlugin.confirm({
|
|
title: "删除跟进提醒",
|
|
content: "删除后列表中将不再显示,确定删除这条跟进提醒吗?",
|
|
cancelBtn: "取消",
|
|
confirmBtn: "删除",
|
|
onCancel() {
|
|
dialog.destroy();
|
|
},
|
|
onConfirm: async () => {
|
|
dialog.destroy();
|
|
await remove(item.id || "");
|
|
}
|
|
});
|
|
}
|
|
|
|
async function remove(id: string): Promise<void> {
|
|
operatingId.value = id;
|
|
operatingType.value = "delete";
|
|
try {
|
|
await FollowUpReminderAPI.remove(id);
|
|
pageResult.list = pageResult.list.filter(item => item.id !== id);
|
|
pageResult.total = Math.max(0, pageResult.total - 1);
|
|
Message.success("跟进提醒已删除");
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "跟进提醒删除失败"));
|
|
} finally {
|
|
operatingId.value = "";
|
|
operatingType.value = undefined;
|
|
}
|
|
}
|
|
|
|
// ---------- 导航和生命周期 ----------
|
|
|
|
async function toCreate(): Promise<void> {
|
|
await router.push({
|
|
name: "CustomerFollowUpReminderCreate",
|
|
query: selectedCustomerId.value ? { customerId: selectedCustomerId.value } : undefined
|
|
});
|
|
}
|
|
|
|
async function toDetail(item: FollowUpReminder): Promise<void> {
|
|
if (item.id) {
|
|
await router.push({ name: "CustomerFollowUpReminderDetail", params: { id: item.id } });
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
navBarStore.setCustomRightVisible(canCreate.value);
|
|
try {
|
|
await loadStoreScope();
|
|
const queryCustomerId = String(route.query.customerId || "");
|
|
if (queryCustomerId) {
|
|
selectedCustomerId.value = queryCustomerId;
|
|
selectedCustomer.value = await loadCustomer(queryCustomerId);
|
|
customerSearchKeyword.value = getCustomerSearchKeyword(selectedCustomer.value);
|
|
}
|
|
await fetch();
|
|
} catch (error) {
|
|
Message.error(getErrorMessage(error, "跟进提醒列表加载失败"));
|
|
}
|
|
});
|
|
|
|
watch(() => route.query.customerId, async value => {
|
|
const id = String(value || "");
|
|
if (id === selectedCustomerId.value) {
|
|
return;
|
|
}
|
|
selectedCustomerId.value = id;
|
|
selectedCustomer.value = id ? await loadCustomer(id) : undefined;
|
|
customerSearchKeyword.value = getCustomerSearchKeyword(selectedCustomer.value);
|
|
await refresh();
|
|
});
|
|
|
|
onUnmounted(() => navBarStore.clearRight());
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.follow-up-reminder-list {
|
|
padding: var(--app-nav-height) 0 1rem;
|
|
|
|
.filter {
|
|
top: var(--app-nav-height);
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 10;
|
|
display: flex;
|
|
padding: .75rem 1rem;
|
|
position: sticky;
|
|
background: #f5f5f8;
|
|
box-shadow: var(--td-shadow-1);
|
|
flex-direction: column;
|
|
|
|
.customer {
|
|
--td-button-border-radius: .75rem;
|
|
--td-input-vertical-padding: .25rem 1rem;
|
|
|
|
width: 100%;
|
|
border: 1px solid var(--td-component-border);
|
|
background: var(--td-bg-color-container);
|
|
padding-right: .3rem;
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.menus {
|
|
--td-spacer-1: 6px;
|
|
|
|
width: 100%;
|
|
border: 1px solid rgba(148, 163, 184, .18);
|
|
z-index: -1;
|
|
overflow: hidden;
|
|
margin-top: -1rem;
|
|
background: #fff;
|
|
border-top: none;
|
|
padding-top: .8rem;
|
|
border-radius: 0 0 1rem 1rem;
|
|
}
|
|
|
|
}
|
|
|
|
.selected-filter {
|
|
gap: .5rem;
|
|
width: 100%;
|
|
min-width: 0;
|
|
display: flex;
|
|
overflow: auto;
|
|
align-items: center;
|
|
|
|
:deep(.t-tag) {
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.t-button {
|
|
flex: 0 0 auto;
|
|
}
|
|
}
|
|
|
|
.list-wrap {
|
|
padding: 1rem 0;
|
|
|
|
.summary {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.items {
|
|
|
|
.item {
|
|
|
|
.cell {
|
|
padding: 10px;
|
|
min-width: 0;
|
|
|
|
:deep(.t-cell__title),
|
|
:deep(.t-cell__title-text) {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.avatar {
|
|
width: 3.5rem;
|
|
height: 3.5rem;
|
|
}
|
|
|
|
.title {
|
|
gap: .5rem;
|
|
width: 100%;
|
|
display: flex;
|
|
min-width: 0;
|
|
font-size: .875rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
|
|
.name-channel {
|
|
gap: .5rem;
|
|
display: flex;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
align-items: center;
|
|
|
|
> .text-bold {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
}
|
|
|
|
.description {
|
|
font-size: .75rem;
|
|
width: 100%;
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
line-height: 1.25;
|
|
|
|
.remind-time {
|
|
font-weight: 600;
|
|
|
|
&.near {
|
|
color: var(--td-error-color);
|
|
}
|
|
|
|
&.soon {
|
|
color: var(--td-warning-color);
|
|
}
|
|
|
|
&.later {
|
|
color: var(--td-success-color);
|
|
}
|
|
}
|
|
|
|
.content {
|
|
color: var(--td-text-color-secondary);
|
|
width: 100%;
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.time-responsible {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.swipe-actions {
|
|
--td-button-border-radius: 0;
|
|
|
|
.complete {
|
|
color: #fff;
|
|
background: var(--td-success-color);
|
|
}
|
|
|
|
.delete {
|
|
color: #fff;
|
|
background: var(--td-error-color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
:global(html.theme-dark) {
|
|
.follow-up-reminder-list {
|
|
.filter {
|
|
background: #10161f;
|
|
}
|
|
|
|
.filter .menus {
|
|
background: #172033;
|
|
}
|
|
|
|
.summary,
|
|
.items .item,
|
|
.items .cell {
|
|
color: #e2e8f0;
|
|
background: rgba(30, 41, 59, .9);
|
|
border-color: rgba(148, 163, 184, .16);
|
|
}
|
|
|
|
.description .content {
|
|
color: #94a3b8;
|
|
}
|
|
|
|
}
|
|
}
|
|
</style>
|