refactor event
This commit is contained in:
+64
-21
@@ -1,6 +1,6 @@
|
|||||||
import { axios, type Page, type PageResult } from "timi-web";
|
import { axios, type Page, type PageResult } from "timi-web";
|
||||||
import type { Customer, CustomerDailyStatView, CustomerGenderStatView, CustomerInviteUpdateReq, CustomerSearchReq, CustomerTrendStatReq, QuickRegisterReq } from "@/types/Customer";
|
import type { Customer, CustomerDailyStatView, CustomerGenderStatView, CustomerInviteUpdateReq, CustomerSearchReq, CustomerTrendStatReq, QuickRegisterReq } from "@/types/Customer";
|
||||||
import type { RegistrationEventRecord } from "@/types/RegistrationEventRecord";
|
import type { EventRecord } from "@/types/EventRecord";
|
||||||
|
|
||||||
const BASE_URI = "/gao/customer";
|
const BASE_URI = "/gao/customer";
|
||||||
|
|
||||||
@@ -9,15 +9,26 @@ async function list(req: Page<Customer>): Promise<PageResult<Customer>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function search(req: CustomerSearchReq): Promise<Customer[]> {
|
async function search(req: CustomerSearchReq): Promise<Customer[]> {
|
||||||
return await axios.post(`${BASE_URI}/search`, req);
|
const [codeMatched, nameMatched] = await Promise.all([
|
||||||
|
req.keyword ? nullableDetailByCode(req.keyword) : undefined,
|
||||||
|
req.keyword ? nullableDetailByName(req.keyword) : undefined
|
||||||
|
]);
|
||||||
|
return [codeMatched, nameMatched].filter((item, index, list): item is Customer => !!item && index === list.findIndex(current => current?.id === item.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchPage(req: Page<CustomerSearchReq>): Promise<PageResult<Customer>> {
|
async function searchPage(req: Page<CustomerSearchReq>): Promise<PageResult<Customer>> {
|
||||||
return await axios.post(`${BASE_URI}/search/page`, req);
|
return await axios.post(`${BASE_URI}/list`, {
|
||||||
|
...req,
|
||||||
|
equalsExample: buildCustomerListExample(req.equalsExample)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportExcel(req: CustomerSearchReq): Promise<Blob> {
|
async function exportExcel(req: CustomerSearchReq): Promise<Blob> {
|
||||||
return await axios.post(`${BASE_URI}/export`, req, {
|
return await axios.post(`${BASE_URI}/export`, {
|
||||||
|
index: 0,
|
||||||
|
size: 100000,
|
||||||
|
equalsExample: buildCustomerListExample(req)
|
||||||
|
}, {
|
||||||
responseType: "blob"
|
responseType: "blob"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -28,15 +39,15 @@ async function detail(id: string): Promise<Customer> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detailByCode(code: string): Promise<Customer> {
|
async function nullableDetailByCode(code: string): Promise<Customer | null> {
|
||||||
return await axios.post(`${BASE_URI}/detail/code`, undefined, {
|
return await axios.post(`${BASE_URI}/find/code`, undefined, {
|
||||||
params: { code }
|
params: { code }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function nullableDetailByCode(code: string): Promise<Customer | null> {
|
async function nullableDetailByName(name: string): Promise<Customer | null> {
|
||||||
return await axios.post(`${BASE_URI}/detail/code/nullable`, undefined, {
|
return await axios.post(`${BASE_URI}/find/name`, undefined, {
|
||||||
params: { code }
|
params: { name }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,11 +58,29 @@ async function invitedList(introducerCustomerId: string): Promise<Customer[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function create(req: Customer): Promise<Customer> {
|
async function create(req: Customer): Promise<Customer> {
|
||||||
return await axios.post(`${BASE_URI}/create`, req);
|
const id = await axios.post(`${BASE_URI}/create`, req) as unknown as string;
|
||||||
|
return {
|
||||||
|
...req,
|
||||||
|
id
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function quickRegister(req: QuickRegisterReq): Promise<RegistrationEventRecord> {
|
async function quickRegister(req: QuickRegisterReq): Promise<EventRecord> {
|
||||||
return await axios.post(`${BASE_URI}/quick/register`, req);
|
const customer = await nullableDetailByCode(req.customerCode);
|
||||||
|
if (!customer?.id) {
|
||||||
|
throw new Error("客户不存在,无法登记");
|
||||||
|
}
|
||||||
|
const record: EventRecord = {
|
||||||
|
customerId: customer.id,
|
||||||
|
eventId: req.eventId,
|
||||||
|
remark: req.remark,
|
||||||
|
attachmentIdList: req.attachmentIdList
|
||||||
|
};
|
||||||
|
await axios.post("/gao/event/record/create", {
|
||||||
|
...record,
|
||||||
|
eventId: req.eventId
|
||||||
|
});
|
||||||
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(req: Customer): Promise<void> {
|
async function update(req: Customer): Promise<void> {
|
||||||
@@ -64,12 +93,17 @@ async function remove(id: string): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInvite(req: CustomerInviteUpdateReq): Promise<void> {
|
|
||||||
return await axios.post(`${BASE_URI}/invite/create`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateInvite(req: CustomerInviteUpdateReq): Promise<void> {
|
async function updateInvite(req: CustomerInviteUpdateReq): Promise<void> {
|
||||||
return await axios.post(`${BASE_URI}/invite/update`, req);
|
if (!req.introducerCustomerId) {
|
||||||
|
await deleteInvite(req.customerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return await axios.post(`${BASE_URI}/invite/save`, undefined, {
|
||||||
|
params: {
|
||||||
|
customerId: req.customerId,
|
||||||
|
introducerCustomerId: req.introducerCustomerId
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteInvite(customerId: string): Promise<void> {
|
async function deleteInvite(customerId: string): Promise<void> {
|
||||||
@@ -79,11 +113,20 @@ async function deleteInvite(customerId: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function dailyStat(req: CustomerTrendStatReq): Promise<CustomerDailyStatView[]> {
|
async function dailyStat(req: CustomerTrendStatReq): Promise<CustomerDailyStatView[]> {
|
||||||
return await axios.post(`${BASE_URI}/stat/daily`, req);
|
return await axios.post(`${BASE_URI}/state/daily`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genderStat(): Promise<CustomerGenderStatView[]> {
|
async function genderStat(): Promise<CustomerGenderStatView[]> {
|
||||||
return await axios.post(`${BASE_URI}/stat/gender`);
|
return await axios.post(`${BASE_URI}/state/gender`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCustomerListExample(req?: CustomerSearchReq): Partial<Customer> {
|
||||||
|
if (!req?.introducerCustomerId) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
introducerCustomerId: req.introducerCustomerId
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -92,16 +135,16 @@ export default {
|
|||||||
searchPage,
|
searchPage,
|
||||||
exportExcel,
|
exportExcel,
|
||||||
detail,
|
detail,
|
||||||
detailByCode,
|
|
||||||
nullableDetailByCode,
|
nullableDetailByCode,
|
||||||
|
nullableDetailByName,
|
||||||
invitedList,
|
invitedList,
|
||||||
create,
|
create,
|
||||||
quickRegister,
|
quickRegister,
|
||||||
update,
|
update,
|
||||||
remove,
|
remove,
|
||||||
createInvite,
|
|
||||||
updateInvite,
|
updateInvite,
|
||||||
deleteInvite,
|
deleteInvite,
|
||||||
dailyStat,
|
dailyStat,
|
||||||
genderStat
|
genderStat
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { axios, type Page, type PageResult } from "timi-web";
|
import { axios, type Page, type PageResult } from "timi-web";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
|
|
||||||
const BASE_URI = "/gao/registration-event";
|
const BASE_URI = "/gao/event";
|
||||||
|
|
||||||
async function list(req: Page<RegistrationEvent>): Promise<PageResult<RegistrationEvent>> {
|
async function list(req: Page<EventItem>): Promise<PageResult<EventItem>> {
|
||||||
return await axios.post(`${BASE_URI}/list`, req);
|
return await axios.post(`${BASE_URI}/list`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enabledList(): Promise<RegistrationEvent[]> {
|
async function enabledList(): Promise<EventItem[]> {
|
||||||
return await axios.post(`${BASE_URI}/enabled/list`);
|
return await axios.post(`${BASE_URI}/list/valid`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detail(id: string): Promise<RegistrationEvent> {
|
async function detail(id: string): Promise<EventItem> {
|
||||||
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
||||||
params: { id }
|
params: { id }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create(req: RegistrationEvent): Promise<void> {
|
async function create(req: EventItem): Promise<void> {
|
||||||
return await axios.post(`${BASE_URI}/create`, req);
|
return await axios.post(`${BASE_URI}/create`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(req: RegistrationEvent): Promise<void> {
|
async function update(req: EventItem): Promise<void> {
|
||||||
return await axios.post(`${BASE_URI}/update`, req);
|
return await axios.post(`${BASE_URI}/update`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,3 +44,4 @@ export default {
|
|||||||
sort,
|
sort,
|
||||||
remove
|
remove
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import { axios, type Page, type PageResult } from "timi-web";
|
||||||
|
import type {
|
||||||
|
CustomerEventRankResponse,
|
||||||
|
CustomerEventRankView,
|
||||||
|
EventRecord,
|
||||||
|
EventRecordCalendarView,
|
||||||
|
EventRecordCreateReq,
|
||||||
|
EventRecordDailyStatView,
|
||||||
|
EventRecordListItemView,
|
||||||
|
EventRecordPage,
|
||||||
|
EventRecordPageReq,
|
||||||
|
EventRecordTrendStatReq
|
||||||
|
} from "@/types/EventRecord";
|
||||||
|
|
||||||
|
const BASE_URI = "/gao/event/record";
|
||||||
|
|
||||||
|
async function list(req: Page<EventRecord>): Promise<PageResult<EventRecord>> {
|
||||||
|
return await axios.post(`${BASE_URI}/list`, buildRecordPage(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detail(id: string): Promise<EventRecord> {
|
||||||
|
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rangeList(req: EventRecordPage): Promise<EventRecord[]> {
|
||||||
|
const result: PageResult<EventRecord> = await axios.post(`${BASE_URI}/list`, buildRecordPage({
|
||||||
|
index: 0,
|
||||||
|
size: 1000,
|
||||||
|
equalsExample: req
|
||||||
|
}));
|
||||||
|
return result.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calendarRangeList(req: EventRecordPage): Promise<EventRecordCalendarView[]> {
|
||||||
|
const result = await rangeList(req);
|
||||||
|
return result.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
customerId: item.customerId,
|
||||||
|
customerCode: item.customer?.code || "",
|
||||||
|
customerName: item.customer?.name || "",
|
||||||
|
eventId: item.eventId,
|
||||||
|
registeredAt: item.registeredAt
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rangePage(req: Page<EventRecordPage>): Promise<PageResult<EventRecordListItemView>> {
|
||||||
|
const result: PageResult<EventRecord> = await axios.post(`${BASE_URI}/list`, buildRecordPage(req));
|
||||||
|
return {
|
||||||
|
total: result.total,
|
||||||
|
list: result.list.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
customerId: item.customerId,
|
||||||
|
customerName: item.customer?.name || "",
|
||||||
|
customerPhotoAttachmentId: item.customer?.attachmentList?.[0]?.id,
|
||||||
|
eventId: item.eventId,
|
||||||
|
eventName: item.event?.name || "",
|
||||||
|
registeredAt: item.registeredAt
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rangeExport(req: EventRecordPage): Promise<Blob> {
|
||||||
|
return await axios.post(`${BASE_URI}/export`, buildRecordPage({
|
||||||
|
index: 0,
|
||||||
|
size: 100000,
|
||||||
|
equalsExample: req
|
||||||
|
}), {
|
||||||
|
responseType: "blob"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create(req: EventRecordCreateReq): Promise<EventRecord[]> {
|
||||||
|
const recordList = req.customerIdList.map(customerId => ({
|
||||||
|
customerId,
|
||||||
|
eventId: req.eventId,
|
||||||
|
remark: req.remark,
|
||||||
|
registeredAt: req.registeredAt,
|
||||||
|
attachmentIdList: req.attachmentIdList
|
||||||
|
}));
|
||||||
|
for (const record of recordList) {
|
||||||
|
await axios.post(`${BASE_URI}/create`, record);
|
||||||
|
}
|
||||||
|
return recordList;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update(req: EventRecord): Promise<void> {
|
||||||
|
await axios.post(`${BASE_URI}/update`, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove(id: string): Promise<void> {
|
||||||
|
await axios.post(`${BASE_URI}/delete`, undefined, {
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function customerRegistrationRank(req?: Partial<EventRecordPage>): Promise<CustomerEventRankView[]> {
|
||||||
|
const result: PageResult<CustomerEventRankResponse> = await axios.post(`${BASE_URI}/rank`, buildRecordPage({
|
||||||
|
index: 0,
|
||||||
|
size: 100,
|
||||||
|
equalsExample: req
|
||||||
|
}));
|
||||||
|
return result.list.map(item => ({
|
||||||
|
rank: item.rank,
|
||||||
|
customerId: item.customerId,
|
||||||
|
customerCode: item.customer?.code || "",
|
||||||
|
customerName: item.customer?.name || "",
|
||||||
|
customerPhotoAttachmentId: item.customer?.attachmentList?.[0]?.id,
|
||||||
|
totalEventDays: item.totalEventDays,
|
||||||
|
totalEventCount: item.totalEventCount,
|
||||||
|
recentContinuousEventDays: item.recentContinuousEventDays,
|
||||||
|
latestRegisteredAt: item.latestRegisteredAt
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function allCustomerEventDailyStat(req: EventRecordTrendStatReq): Promise<EventRecordDailyStatView[]> {
|
||||||
|
void req;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRecordPage(req: Page<EventRecord | EventRecordPage> | Page<Partial<EventRecordPage>>): EventRecordPageReq {
|
||||||
|
const equalsExample = req.equalsExample || {};
|
||||||
|
const eventIdList = "eventIdList" in equalsExample ? equalsExample.eventIdList : undefined;
|
||||||
|
const singleEventId = "eventId" in equalsExample ? equalsExample.eventId : undefined;
|
||||||
|
return {
|
||||||
|
index: req.index,
|
||||||
|
size: req.size,
|
||||||
|
customerId: equalsExample.customerId,
|
||||||
|
eventIdList: eventIdList || (singleEventId ? [singleEventId] : undefined),
|
||||||
|
keyword: "keyword" in equalsExample ? equalsExample.keyword : undefined,
|
||||||
|
beginAt: "beginAt" in equalsExample ? equalsExample.beginAt : undefined,
|
||||||
|
endAt: "endAt" in equalsExample ? equalsExample.endAt : undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
list,
|
||||||
|
detail,
|
||||||
|
rangeList,
|
||||||
|
calendarRangeList,
|
||||||
|
rangePage,
|
||||||
|
rangeExport,
|
||||||
|
create,
|
||||||
|
update,
|
||||||
|
remove,
|
||||||
|
customerRegistrationRank,
|
||||||
|
allCustomerEventDailyStat
|
||||||
|
};
|
||||||
|
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import { axios, type Page, type PageResult, type Role } from "timi-web";
|
import { axios, type Page, type PageResult, type Role } from "timi-web";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
|
|
||||||
const BASE_URI = "/gao/user";
|
const BASE_URI = "/gao/user";
|
||||||
|
|
||||||
async function list(req: Page<GaoUser>): Promise<PageResult<GaoUser>> {
|
async function list(req: Page<ManagedUser>): Promise<PageResult<ManagedUser>> {
|
||||||
return await axios.post(`${BASE_URI}/list`, req);
|
return await axios.post(`${BASE_URI}/list`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detail(id: string): Promise<GaoUser> {
|
async function detail(id: string): Promise<ManagedUser> {
|
||||||
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
||||||
params: { id }
|
params: { id }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function create(req: GaoUser): Promise<GaoUser> {
|
async function create(req: ManagedUser): Promise<ManagedUser> {
|
||||||
return await axios.post(`${BASE_URI}/create`, req);
|
return await axios.post(`${BASE_URI}/create`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(req: GaoUser): Promise<GaoUser> {
|
async function update(req: ManagedUser): Promise<ManagedUser> {
|
||||||
return await axios.post(`${BASE_URI}/update`, req);
|
return await axios.post(`${BASE_URI}/update`, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,3 +39,4 @@ export default {
|
|||||||
remove,
|
remove,
|
||||||
roleList
|
roleList
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import { axios, type Page, type PageResult } from "timi-web";
|
|
||||||
import type {
|
|
||||||
CustomerRegisterStatView,
|
|
||||||
CustomerRegistrationRankView,
|
|
||||||
RegistrationEventRecordCalendarView,
|
|
||||||
RegistrationEventRecord,
|
|
||||||
RegistrationEventRecordCreateReq,
|
|
||||||
RegistrationEventRecordDailyStatView,
|
|
||||||
RegistrationEventRecordListItemView,
|
|
||||||
RegistrationEventRecordRangeReq,
|
|
||||||
RegistrationEventRecordStatReq,
|
|
||||||
RegistrationEventRecordTrendStatReq
|
|
||||||
} from "@/types/RegistrationEventRecord";
|
|
||||||
|
|
||||||
const BASE_URI = "/gao/registration-event-record";
|
|
||||||
|
|
||||||
async function list(req: Page<RegistrationEventRecord>): Promise<PageResult<RegistrationEventRecord>> {
|
|
||||||
return await axios.post(`${BASE_URI}/list`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function detail(id: string): Promise<RegistrationEventRecord> {
|
|
||||||
return await axios.post(`${BASE_URI}/detail`, undefined, {
|
|
||||||
params: { id }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rangeList(req: RegistrationEventRecordRangeReq): Promise<RegistrationEventRecord[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/range/list`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function calendarRangeList(req: RegistrationEventRecordRangeReq): Promise<RegistrationEventRecordCalendarView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/range/calendar/list`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rangePage(req: Page<RegistrationEventRecordRangeReq>): Promise<PageResult<RegistrationEventRecordListItemView>> {
|
|
||||||
return await axios.post(`${BASE_URI}/range/page`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rangeExport(req: RegistrationEventRecordRangeReq): Promise<Blob> {
|
|
||||||
return await axios.post(`${BASE_URI}/range/export`, req, {
|
|
||||||
responseType: "blob"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function create(req: RegistrationEventRecordCreateReq): Promise<RegistrationEventRecord[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/create`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update(req: RegistrationEventRecord): Promise<void> {
|
|
||||||
await axios.post(`${BASE_URI}/update`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function remove(id: string): Promise<void> {
|
|
||||||
await axios.post(`${BASE_URI}/delete`, undefined, {
|
|
||||||
params: { id }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function customerRangeStat(req: RegistrationEventRecordStatReq): Promise<CustomerRegisterStatView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/customer/range/stat`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function allCustomerRangeStat(req: RegistrationEventRecordStatReq): Promise<CustomerRegisterStatView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/all-customer/range/stat`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function customerRegistrationRank(req?: Partial<RegistrationEventRecordRangeReq>): Promise<CustomerRegistrationRankView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/customer/registration-rank`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function customerRegistrationEventDailyStat(req: RegistrationEventRecordTrendStatReq): Promise<RegistrationEventRecordDailyStatView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/customer/registration-event/daily/stat`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function allCustomerRegistrationEventDailyStat(req: RegistrationEventRecordTrendStatReq): Promise<RegistrationEventRecordDailyStatView[]> {
|
|
||||||
return await axios.post(`${BASE_URI}/all-customer/registration-event/daily/stat`, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
list,
|
|
||||||
detail,
|
|
||||||
rangeList,
|
|
||||||
calendarRangeList,
|
|
||||||
rangePage,
|
|
||||||
rangeExport,
|
|
||||||
create,
|
|
||||||
update,
|
|
||||||
remove,
|
|
||||||
customerRangeStat,
|
|
||||||
allCustomerRangeStat,
|
|
||||||
customerRegistrationRank,
|
|
||||||
customerRegistrationEventDailyStat,
|
|
||||||
allCustomerRegistrationEventDailyStat
|
|
||||||
};
|
|
||||||
+31
-30
@@ -14,12 +14,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</t-button>
|
</t-button>
|
||||||
<t-button
|
<t-button
|
||||||
v-if="showRegistrationEventFilter"
|
v-if="showEventFilter"
|
||||||
class="filter-btn"
|
class="filter-btn"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
size="extra-small"
|
size="extra-small"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@click="registrationEventFilterVisible = true"
|
@click="eventFilterVisible = true"
|
||||||
content="登记事件"
|
content="登记事件"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<t-cell
|
<t-cell
|
||||||
v-for="item in selectedDayRecords"
|
v-for="item in selectedDayRecords"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="getRecordRegistrationEventName(item)"
|
:title="getRecordEventName(item)"
|
||||||
:description="getRecordCustomerName(item)"
|
:description="getRecordCustomerName(item)"
|
||||||
:note="`${formatRecordTime(item.registeredAt)}`"
|
:note="`${formatRecordTime(item.registeredAt)}`"
|
||||||
/>
|
/>
|
||||||
@@ -69,19 +69,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t-popup>
|
</t-popup>
|
||||||
<t-popup v-model="registrationEventFilterVisible" placement="bottom">
|
<t-popup v-model="eventFilterVisible" placement="bottom">
|
||||||
<div class="filter-popup">
|
<div class="filter-popup">
|
||||||
<div class="popup-head">
|
<div class="popup-head">
|
||||||
<div class="title">登记事件筛选</div>
|
<div class="title">登记事件筛选</div>
|
||||||
<t-button theme="default" variant="text" size="small" @click="registrationEventFilterVisible = false">
|
<t-button theme="default" variant="text" size="small" @click="eventFilterVisible = false">
|
||||||
关闭
|
关闭
|
||||||
</t-button>
|
</t-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="popup-body">
|
<div class="popup-body">
|
||||||
<t-checkbox-group v-model="selectedRegistrationEventIdListProxy" class="items">
|
<t-checkbox-group v-model="selectedEventIdListProxy" class="items">
|
||||||
<t-checkbox
|
<t-checkbox
|
||||||
class="item"
|
class="item"
|
||||||
v-for="item in registrationEventList"
|
v-for="item in eventList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
@@ -96,8 +96,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {RegistrationEvent} from "@/types/RegistrationEvent";
|
import type {EventItem} from "@/types/EventItem";
|
||||||
import type {RegistrationEventRecord} from "@/types/RegistrationEventRecord";
|
import type {EventRecord} from "@/types/EventRecord";
|
||||||
import {Time} from "timi-web";
|
import {Time} from "timi-web";
|
||||||
|
|
||||||
interface CalendarDay {
|
interface CalendarDay {
|
||||||
@@ -106,56 +106,56 @@ interface CalendarDay {
|
|||||||
dateText: string;
|
dateText: string;
|
||||||
inMonth: boolean;
|
inMonth: boolean;
|
||||||
isToday: boolean;
|
isToday: boolean;
|
||||||
records: RegistrationEventRecord[];
|
records: EventRecord[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
registrationEventList: RegistrationEvent[];
|
eventList: EventItem[];
|
||||||
customerName?: string;
|
customerName?: string;
|
||||||
emptyText?: string;
|
emptyText?: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
modelValue: Date;
|
modelValue: Date;
|
||||||
records: RegistrationEventRecord[];
|
records: EventRecord[];
|
||||||
selectedRegistrationEventIdList: string[];
|
selectedEventIdList: string[];
|
||||||
showRegistrationEventFilter?: boolean;
|
showEventFilter?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
customerName: "",
|
customerName: "",
|
||||||
emptyText: "当前月份暂无匹配登记",
|
emptyText: "当前月份暂无匹配登记",
|
||||||
loading: false,
|
loading: false,
|
||||||
showRegistrationEventFilter: true
|
showEventFilter: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
"update:modelValue": [value: Date];
|
"update:modelValue": [value: Date];
|
||||||
"update:selectedRegistrationEventIdList": [value: string[]];
|
"update:selectedEventIdList": [value: string[]];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const weekNameList = ["日", "一", "二", "三", "四", "五", "六"];
|
const weekNameList = ["日", "一", "二", "三", "四", "五", "六"];
|
||||||
const dayRecordVisible = ref(false);
|
const dayRecordVisible = ref(false);
|
||||||
const registrationEventFilterVisible = ref(false);
|
const eventFilterVisible = ref(false);
|
||||||
const selectedDayTitle = ref("");
|
const selectedDayTitle = ref("");
|
||||||
const selectedDayRecords = ref<RegistrationEventRecord[]>([]);
|
const selectedDayRecords = ref<EventRecord[]>([]);
|
||||||
|
|
||||||
const selectedRegistrationEventIdListProxy = computed({
|
const selectedEventIdListProxy = computed({
|
||||||
get: () => props.selectedRegistrationEventIdList,
|
get: () => props.selectedEventIdList,
|
||||||
set: value => emit("update:selectedRegistrationEventIdList", value)
|
set: value => emit("update:selectedEventIdList", value)
|
||||||
});
|
});
|
||||||
const registrationEventNameMap = computed(() => {
|
const eventNameMap = computed(() => {
|
||||||
const map = new Map<string, string>();
|
const map = new Map<string, string>();
|
||||||
for (const item of props.registrationEventList) {
|
for (const item of props.eventList) {
|
||||||
if (item.id) {
|
if (item.id) {
|
||||||
map.set(item.id, item.name || "未命名事件");
|
map.set(item.id, item.name || "未命名事件");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
const selectedRegistrationEventIdSet = computed(() => new Set(props.selectedRegistrationEventIdList));
|
const selectedEventIdSet = computed(() => new Set(props.selectedEventIdList));
|
||||||
const filteredRecords = computed(() => {
|
const filteredRecords = computed(() => {
|
||||||
if (!props.showRegistrationEventFilter || !props.selectedRegistrationEventIdList.length) {
|
if (!props.showEventFilter || !props.selectedEventIdList.length) {
|
||||||
return props.records;
|
return props.records;
|
||||||
}
|
}
|
||||||
|
|
||||||
return props.records.filter(item => item.registrationEventId && selectedRegistrationEventIdSet.value.has(item.registrationEventId));
|
return props.records.filter(item => item.eventId && selectedEventIdSet.value.has(item.eventId));
|
||||||
});
|
});
|
||||||
const currentMonthFilteredRecords = computed(() => filteredRecords.value.filter(item => isInCurrentMonth(item.registeredAt)));
|
const currentMonthFilteredRecords = computed(() => filteredRecords.value.filter(item => isInCurrentMonth(item.registeredAt)));
|
||||||
const monthTitle = computed(() => `${props.modelValue.getFullYear()} 年 ${props.modelValue.getMonth() + 1} 月`);
|
const monthTitle = computed(() => `${props.modelValue.getFullYear()} 年 ${props.modelValue.getMonth() + 1} 月`);
|
||||||
@@ -192,15 +192,15 @@ function openDayRecords(day: CalendarDay): void {
|
|||||||
dayRecordVisible.value = true;
|
dayRecordVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordRegistrationEventName(record?: RegistrationEventRecord): string {
|
function getRecordEventName(record?: EventRecord): string {
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return record.registrationEvent?.name || (record.registrationEventId ? registrationEventNameMap.value.get(record.registrationEventId) : "") || "未知事件";
|
return record.event?.name || (record.eventId ? eventNameMap.value.get(record.eventId) : "") || "未知事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordCustomerName(record: RegistrationEventRecord): string {
|
function getRecordCustomerName(record: EventRecord): string {
|
||||||
return record.customer?.name || props.customerName || record.customerId || "未知客户";
|
return record.customer?.name || props.customerName || record.customerId || "未知客户";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ function formatRecordTime(value?: number): string {
|
|||||||
return Time.formatDateTime(date);
|
return Time.formatDateTime(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordDateText(record: RegistrationEventRecord): string {
|
function getRecordDateText(record: EventRecord): string {
|
||||||
const date = Time.toDateObject(record.registeredAt);
|
const date = Time.toDateObject(record.registeredAt);
|
||||||
return date ? Time.formatDate(date) : "";
|
return date ? Time.formatDate(date) : "";
|
||||||
}
|
}
|
||||||
@@ -456,3 +456,4 @@ function addMonths(date: Date, months: number): Date {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<p v-if="label" class="t-form__label" :class="{required: requiredMark}" v-text="label" />
|
<p v-if="label" class="t-form__label" :class="{required: requiredMark}" v-text="label" />
|
||||||
<t-checkbox-group v-model="modelValue" class="group">
|
<t-checkbox-group v-model="modelValue" class="group">
|
||||||
<t-checkbox
|
<t-checkbox
|
||||||
v-for="item in registrationEventList"
|
v-for="item in eventList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
class="item"
|
class="item"
|
||||||
@@ -12,15 +12,15 @@
|
|||||||
<span v-text="item.name" />
|
<span v-text="item.name" />
|
||||||
</t-checkbox>
|
</t-checkbox>
|
||||||
</t-checkbox-group>
|
</t-checkbox-group>
|
||||||
<div v-if="!registrationEventList.length" class="empty-block">暂无可用登记事件</div>
|
<div v-if="!eventList.length" class="empty-block">暂无可用登记事件</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
registrationEventList: RegistrationEvent[];
|
eventList: EventItem[];
|
||||||
label?: string;
|
label?: string;
|
||||||
requiredMark?: boolean;
|
requiredMark?: boolean;
|
||||||
}
|
}
|
||||||
@@ -87,3 +87,4 @@ const modelValue = defineModel<string[]>({ required: true });
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.registrationEventRecord.create" class="quick-entry-page">
|
<div v-can="permissionCodes.eventRecord.create" class="quick-entry-page">
|
||||||
<t-form class="form" label-align="left" label-width="6rem">
|
<t-form class="form" label-align="left" label-width="6rem">
|
||||||
<t-form-item label="客户编码" required-mark>
|
<t-form-item label="客户编码" required-mark>
|
||||||
<t-input
|
<t-input
|
||||||
@@ -59,10 +59,10 @@
|
|||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label-width="0">
|
<t-form-item label-width="0">
|
||||||
<registration-event-selector
|
<event-selector
|
||||||
v-model="selectedRegistrationEventIdList"
|
v-model="selectedEventIdList"
|
||||||
class="registration-event-selector"
|
class="registration-event-selector"
|
||||||
:registration-event-list="registrationEventList"
|
:event-list="eventList"
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="登记备注">
|
<t-form-item label="登记备注">
|
||||||
@@ -111,18 +111,18 @@ import { AttachmentAPI } from "timi-web";
|
|||||||
import type { TdInputProps } from "tdesign-mobile-vue";
|
import type { TdInputProps } from "tdesign-mobile-vue";
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import PhotoField from "@/components/PhotoField.vue";
|
import PhotoField from "@/components/PhotoField.vue";
|
||||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
import type { Customer } from "@/types/Customer";
|
import type { Customer } from "@/types/Customer";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import { getCustomerPhotoURL } from "@/utils/customer";
|
import { getCustomerPhotoURL } from "@/utils/customer";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { compressUploadImage, compressUploadImageList } from "@/utils/imageCompression";
|
import { compressUploadImage, compressUploadImageList } from "@/utils/imageCompression";
|
||||||
import RegistrationEventSelector from "@/components/RegistrationEventSelector.vue";
|
import EventSelector from "@/components/EventSelector.vue";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "QuickEntryPage"
|
name: "QuickEntryPage"
|
||||||
@@ -137,8 +137,8 @@ const recordPhotoInputRef = ref<HTMLInputElement>();
|
|||||||
const recordPhotoTempFileIdList = ref<string[]>([]);
|
const recordPhotoTempFileIdList = ref<string[]>([]);
|
||||||
const supplementPhotoUploading = ref(false);
|
const supplementPhotoUploading = ref(false);
|
||||||
const recordPhotoUploading = ref(false);
|
const recordPhotoUploading = ref(false);
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedRegistrationEventIdList = ref<string[]>([]);
|
const selectedEventIdList = ref<string[]>([]);
|
||||||
const note = ref("");
|
const note = ref("");
|
||||||
const photoList = ref<string[]>([]);
|
const photoList = ref<string[]>([]);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
@@ -437,11 +437,11 @@ async function saveRecord(): Promise<void> {
|
|||||||
}
|
}
|
||||||
await applyCustomerName();
|
await applyCustomerName();
|
||||||
await saveCustomerProfile(customerCode);
|
await saveCustomerProfile(customerCode);
|
||||||
if (selectedRegistrationEventIdList.value.length) {
|
if (selectedEventIdList.value.length) {
|
||||||
for (const registrationEventId of selectedRegistrationEventIdList.value) {
|
for (const eventId of selectedEventIdList.value) {
|
||||||
await CustomerAPI.quickRegister({
|
await CustomerAPI.quickRegister({
|
||||||
customerCode,
|
customerCode,
|
||||||
registrationEventId,
|
eventId,
|
||||||
remark: note.value || "前台快速登记",
|
remark: note.value || "前台快速登记",
|
||||||
attachmentIdList: recordPhotoTempFileIdList.value
|
attachmentIdList: recordPhotoTempFileIdList.value
|
||||||
});
|
});
|
||||||
@@ -504,7 +504,7 @@ function showToast(message: string, theme: "success" | "warning" = "success"): v
|
|||||||
// ---------- 初始化 ----------
|
// ---------- 初始化 ----------
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(scanCode, (value) => {
|
watch(scanCode, (value) => {
|
||||||
@@ -588,3 +588,4 @@ watch(scanCode, (value) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.registrationEventRecord.create" class="quick-sign-in-page">
|
<div v-can="permissionCodes.eventRecord.create" class="quick-sign-in-page">
|
||||||
<button class="registration-event-select" type="button" @click="registrationEventSelectorVisible = true">
|
<button class="registration-event-select" type="button" @click="eventSelectorVisible = true">
|
||||||
<span class="required-label">登记事件</span>
|
<span class="required-label">登记事件</span>
|
||||||
<strong v-text="selectedRegistrationEventName" />
|
<strong v-text="selectedEventName" />
|
||||||
<t-icon name="chevron-right" />
|
<t-icon name="chevron-right" />
|
||||||
</button>
|
</button>
|
||||||
<code-scanner
|
<code-scanner
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<t-cell
|
<t-cell
|
||||||
v-for="item in recentRecordList"
|
v-for="item in recentRecordList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="getRecordRegistrationEventName(item)"
|
:title="getRecordEventName(item)"
|
||||||
:description="formatTime(item.registeredAt)"
|
:description="formatTime(item.registeredAt)"
|
||||||
:note="item.remark || '无备注'"
|
:note="item.remark || '无备注'"
|
||||||
arrow
|
arrow
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<t-image
|
<t-image
|
||||||
v-if="getRecordPhotoURL(item)"
|
v-if="getRecordPhotoURL(item)"
|
||||||
:src="getRecordPhotoURL(item)"
|
:src="getRecordPhotoURL(item)"
|
||||||
:alt="getRecordRegistrationEventName(item)"
|
:alt="getRecordEventName(item)"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
class="record-photo"
|
class="record-photo"
|
||||||
/>
|
/>
|
||||||
@@ -51,16 +51,16 @@
|
|||||||
@click="submitRegister"
|
@click="submitRegister"
|
||||||
>登记</t-button>
|
>登记</t-button>
|
||||||
</div>
|
</div>
|
||||||
<t-popup v-model="registrationEventSelectorVisible" placement="bottom">
|
<t-popup v-model="eventSelectorVisible" placement="bottom">
|
||||||
<div class="registration-event-panel">
|
<div class="registration-event-panel">
|
||||||
<div class="title">登记事件</div>
|
<div class="title">登记事件</div>
|
||||||
<t-cell-group v-if="registrationEventList.length" theme="card">
|
<t-cell-group v-if="eventList.length" theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
v-for="item in registrationEventList"
|
v-for="item in eventList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="item.name"
|
:title="item.name"
|
||||||
:note="item.id === selectedRegistrationEventId ? '当前使用' : ''"
|
:note="item.id === selectedEventId ? '当前使用' : ''"
|
||||||
@click="selectRegistrationEvent(item.id || '')"
|
@click="selectEvent(item.id || '')"
|
||||||
/>
|
/>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<div v-else class="empty-block">暂无可用登记事件</div>
|
<div v-else class="empty-block">暂无可用登记事件</div>
|
||||||
@@ -71,16 +71,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
import type { Customer } from "@/types/Customer";
|
import type { Customer } from "@/types/Customer";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import type { RegistrationEventRecord } from "@/types/RegistrationEventRecord";
|
import type { EventRecord } from "@/types/EventRecord";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { getRegistrationEventRecordPhotoURL } from "@/utils/registrationEventRecord";
|
import { getEventRecordPhotoURL } from "@/utils/eventRecord";
|
||||||
import { Time } from "timi-web";
|
import { Time } from "timi-web";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -88,21 +88,21 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const registrationEventStorageKey = "quick-scan-registration-event-id";
|
const EventItemStorageKey = "quick-scan-registration-event-id";
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const scanCode = ref("");
|
const scanCode = ref("");
|
||||||
const scanStatus = ref("正在启动扫码预览。");
|
const scanStatus = ref("正在启动扫码预览。");
|
||||||
const scannerSessionKey = ref(0);
|
const scannerSessionKey = ref(0);
|
||||||
const matchedCustomer = ref<Customer>();
|
const matchedCustomer = ref<Customer>();
|
||||||
const recentRecordList = ref<RegistrationEventRecord[]>([]);
|
const recentRecordList = ref<EventRecord[]>([]);
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedRegistrationEventId = ref("");
|
const selectedEventId = ref("");
|
||||||
const registrationEventSelectorVisible = ref(false);
|
const eventSelectorVisible = ref(false);
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
let latestDecodedText = "";
|
let latestDecodedText = "";
|
||||||
|
|
||||||
const selectedRegistrationEventName = computed(() => registrationEventList.value.find(item => item.id === selectedRegistrationEventId.value)?.name || "请选择登记事件");
|
const selectedEventName = computed(() => eventList.value.find(item => item.id === selectedEventId.value)?.name || "请选择登记事件");
|
||||||
const canSubmitRegister = computed(() => !!matchedCustomer.value && !!selectedRegistrationEventId.value);
|
const canSubmitRegister = computed(() => !!matchedCustomer.value && !!selectedEventId.value);
|
||||||
|
|
||||||
// ---------- 扫码 ----------
|
// ---------- 扫码 ----------
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ async function submitRegister(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
await CustomerAPI.quickRegister({
|
await CustomerAPI.quickRegister({
|
||||||
customerCode: customer.code || scanCode.value,
|
customerCode: customer.code || scanCode.value,
|
||||||
registrationEventId: selectedRegistrationEventId.value,
|
eventId: selectedEventId.value,
|
||||||
remark: "客户快速扫码登记"
|
remark: "客户快速扫码登记"
|
||||||
});
|
});
|
||||||
scanStatus.value = `登记成功:${customer.name || customer.code || scanCode.value}。`;
|
scanStatus.value = `登记成功:${customer.name || customer.code || scanCode.value}。`;
|
||||||
@@ -160,7 +160,7 @@ async function submitRegister(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateForm(): boolean {
|
function validateForm(): boolean {
|
||||||
if (!selectedRegistrationEventId.value) {
|
if (!selectedEventId.value) {
|
||||||
showToast("请先选择登记事件", "warning");
|
showToast("请先选择登记事件", "warning");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -192,10 +192,10 @@ async function loadRecentRecordList(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
recentRecordList.value = (await RegistrationEventRecordAPI.rangeList({
|
recentRecordList.value = (await EventRecordAPI.rangeList({
|
||||||
customerId,
|
customerId,
|
||||||
beginRegisteredAt: 0,
|
beginAt: 0,
|
||||||
endRegisteredAt: Time.toTimestamp(new Date())
|
endAt: Time.toTimestamp(new Date())
|
||||||
}))
|
}))
|
||||||
.sort((left, right) => (right.registeredAt || 0) - (left.registeredAt || 0))
|
.sort((left, right) => (right.registeredAt || 0) - (left.registeredAt || 0))
|
||||||
.slice(0, 5);
|
.slice(0, 5);
|
||||||
@@ -208,12 +208,12 @@ async function goRecordDetail(id: string): Promise<void> {
|
|||||||
await router.push(`/management/registration-event-records/${id}`);
|
await router.push(`/management/registration-event-records/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordRegistrationEventName(record: RegistrationEventRecord): string {
|
function getRecordEventName(record: EventRecord): string {
|
||||||
return record.registrationEvent?.name || "未知事件";
|
return record.event?.name || "未知事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordPhotoURL(record: RegistrationEventRecord): string {
|
function getRecordPhotoURL(record: EventRecord): string {
|
||||||
return getRegistrationEventRecordPhotoURL(record);
|
return getEventRecordPhotoURL(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(value?: number): string {
|
function formatTime(value?: number): string {
|
||||||
@@ -231,43 +231,43 @@ function formatTime(value?: number): string {
|
|||||||
// ---------- 登记事件 ----------
|
// ---------- 登记事件 ----------
|
||||||
|
|
||||||
async function loadActions(): Promise<void> {
|
async function loadActions(): Promise<void> {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
const storedRegistrationEventId = loadStoredRegistrationEventId();
|
const storedEventId = loadStoredEventId();
|
||||||
const storedAction = registrationEventList.value.find(item => item.id === storedRegistrationEventId);
|
const storedAction = eventList.value.find(item => item.id === storedEventId);
|
||||||
selectedRegistrationEventId.value = storedAction?.id || registrationEventList.value[0]?.id || "";
|
selectedEventId.value = storedAction?.id || eventList.value[0]?.id || "";
|
||||||
saveStoredRegistrationEventId(selectedRegistrationEventId.value);
|
saveStoredEventId(selectedEventId.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRegistrationEvent(registrationEventId: string | number | Array<string | number>): void {
|
function selectEvent(eventId: string | number | Array<string | number>): void {
|
||||||
if (Array.isArray(registrationEventId)) {
|
if (Array.isArray(eventId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextRegistrationEventId = String(registrationEventId);
|
const nextEventId = String(eventId);
|
||||||
if (!nextRegistrationEventId) {
|
if (!nextEventId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedRegistrationEventId.value = nextRegistrationEventId;
|
selectedEventId.value = nextEventId;
|
||||||
saveStoredRegistrationEventId(nextRegistrationEventId);
|
saveStoredEventId(nextEventId);
|
||||||
registrationEventSelectorVisible.value = false;
|
eventSelectorVisible.value = false;
|
||||||
showToast("登记事件已切换");
|
showToast("登记事件已切换");
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadStoredRegistrationEventId(): string {
|
function loadStoredEventId(): string {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.localStorage.getItem(registrationEventStorageKey) || "";
|
return window.localStorage.getItem(EventItemStorageKey) || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveStoredRegistrationEventId(registrationEventId: string): void {
|
function saveStoredEventId(eventId: string): void {
|
||||||
if (typeof window === "undefined" || !registrationEventId) {
|
if (typeof window === "undefined" || !eventId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.localStorage.setItem(registrationEventStorageKey, registrationEventId);
|
window.localStorage.setItem(EventItemStorageKey, eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showToast(message: string, theme: "success" | "warning" = "success"): void {
|
function showToast(message: string, theme: "success" | "warning" = "success"): void {
|
||||||
@@ -439,3 +439,4 @@ onMounted(loadActions);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.stat.read" class="customer-data-charts-page">
|
<div v-can="permissionCodes.stat.read" class="customer-data-charts-page">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="selector bg-white" :class="{ 'selected' : selectedCustomer }">
|
<div class="selector bg-white" :class="{ 'selected' : selectedCustomer }">
|
||||||
<div class="line buttons">
|
<div class="line buttons">
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
@change="selectRankDateFilter"
|
@change="selectRankDateFilter"
|
||||||
/>
|
/>
|
||||||
<t-dropdown-item
|
<t-dropdown-item
|
||||||
v-model="rankRegistrationEventIdList"
|
v-model="rankEventIdList"
|
||||||
:label="rankRegistrationEventFilterLabel"
|
:label="rankEventFilterLabel"
|
||||||
:options="rankRegistrationEventOptions"
|
:options="rankEventOptions"
|
||||||
options-columns="2"
|
options-columns="2"
|
||||||
multiple
|
multiple
|
||||||
@change="reloadCustomerRegistrationRank"
|
@change="reloadCustomerRegistrationRank"
|
||||||
@@ -56,12 +56,12 @@
|
|||||||
<strong v-text="item.value" />
|
<strong v-text="item.value" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<registration-event-record-calendar
|
<event-record-calendar
|
||||||
v-if="selectedCustomer"
|
v-if="selectedCustomer"
|
||||||
v-model="monthCursor"
|
v-model="monthCursor"
|
||||||
v-model:selected-registration-event-id-list="selectedRegistrationEventIdList"
|
v-model:selected-event-id-list="selectedEventIdList"
|
||||||
class="calendar-section"
|
class="calendar-section"
|
||||||
:registration-event-list="registrationEventList"
|
:event-list="eventList"
|
||||||
:customer-name="selectedCustomer.name"
|
:customer-name="selectedCustomer.name"
|
||||||
:loading="calendarLoading"
|
:loading="calendarLoading"
|
||||||
:records="monthRecords"
|
:records="monthRecords"
|
||||||
@@ -115,11 +115,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
<div>
|
<div>
|
||||||
<span v-text="`累计登记 ${item.totalRegistrationCount || 0} 次,`" />
|
<span v-text="`累计登记 ${item.totalEventCount || 0} 次,`" />
|
||||||
<span v-text="`累计登记 ${item.totalRegistrationDays || 0} 天`" />
|
<span v-text="`累计登记 ${item.totalEventDays || 0} 天`" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span v-text="`最近 ${item.recentContinuousRegistrationDays || 0} 天连续登记`" />
|
<span v-text="`最近 ${item.recentContinuousEventDays || 0} 天连续登记`" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
@@ -165,16 +165,16 @@ import {init, use} from "echarts/core";
|
|||||||
import {CanvasRenderer} from "echarts/renderers";
|
import {CanvasRenderer} from "echarts/renderers";
|
||||||
import {AttachmentAPI, Time} from "timi-web";
|
import {AttachmentAPI, Time} from "timi-web";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import type {Customer} from "@/types/Customer";
|
import type {Customer} from "@/types/Customer";
|
||||||
import type {RegistrationEvent} from "@/types/RegistrationEvent";
|
import type {EventItem} from "@/types/EventItem";
|
||||||
import type {CustomerRegistrationRankView, RegistrationEventRecord, RegistrationEventRecordRangeReq} from "@/types/RegistrationEventRecord";
|
import type {CustomerEventRankView, EventRecord, EventRecordPage} from "@/types/EventRecord";
|
||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import RegistrationEventRecordCalendar from "@/components/RegistrationEventRecordCalendar.vue";
|
import EventRecordCalendar from "@/components/EventRecordCalendar.vue";
|
||||||
import {useSettingStore} from "@/store/settingStore";
|
import {useSettingStore} from "@/store/settingStore";
|
||||||
import {gaoPermissionCodes} from "@/utils/gaoPermission";
|
import {permissionCodes} from "@/utils/permission";
|
||||||
|
|
||||||
type RankDateFilterType = "all" | "today" | "thisMonth" | "custom";
|
type RankDateFilterType = "all" | "today" | "thisMonth" | "custom";
|
||||||
|
|
||||||
@@ -221,43 +221,43 @@ const selectorVisible = ref(false);
|
|||||||
const scannerVisible = ref(false);
|
const scannerVisible = ref(false);
|
||||||
const scanStatus = ref("点击扫码开始识别。");
|
const scanStatus = ref("点击扫码开始识别。");
|
||||||
const selectedCustomer = ref<Customer>();
|
const selectedCustomer = ref<Customer>();
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedRegistrationEventIdList = ref<string[]>([]);
|
const selectedEventIdList = ref<string[]>([]);
|
||||||
const monthCursor = ref(getMonthStart(new Date()));
|
const monthCursor = ref(getMonthStart(new Date()));
|
||||||
const monthRecords = ref<RegistrationEventRecord[]>([]);
|
const monthRecords = ref<EventRecord[]>([]);
|
||||||
const calendarLoading = ref(false);
|
const calendarLoading = ref(false);
|
||||||
const customerRegistrationRankList = ref<CustomerRegistrationRankView[]>([]);
|
const customerRegistrationRankList = ref<CustomerEventRankView[]>([]);
|
||||||
const rankPageSize = 10;
|
const rankPageSize = 10;
|
||||||
const rankPageIndex = ref(1);
|
const rankPageIndex = ref(1);
|
||||||
const rankDateFilterType = ref<RankDateFilterType>("all");
|
const rankDateFilterType = ref<RankDateFilterType>("all");
|
||||||
const rankDateRange = ref(buildRankPresetRange("all"));
|
const rankDateRange = ref(buildRankPresetRange("all"));
|
||||||
const rankRegistrationEventIdList = ref<string[]>([]);
|
const rankEventIdList = ref<string[]>([]);
|
||||||
const rankLoading = ref(false);
|
const rankLoading = ref(false);
|
||||||
const rankRefreshing = ref(false);
|
const rankRefreshing = ref(false);
|
||||||
const rankStartPickerVisible = ref(false);
|
const rankStartPickerVisible = ref(false);
|
||||||
const rankEndPickerVisible = ref(false);
|
const rankEndPickerVisible = ref(false);
|
||||||
const rankCache = new Map<string, CustomerRegistrationRankView[]>();
|
const rankCache = new Map<string, CustomerEventRankView[]>();
|
||||||
const rankPendingRequestMap = new Map<string, Promise<CustomerRegistrationRankView[]>>();
|
const rankPendingRequestMap = new Map<string, Promise<CustomerEventRankView[]>>();
|
||||||
let rankRequestToken = 0;
|
let rankRequestToken = 0;
|
||||||
let latestDecodedText = "";
|
let latestDecodedText = "";
|
||||||
|
|
||||||
const registrationEventNameMap = computed(() => {
|
const eventNameMap = computed(() => {
|
||||||
const map = new Map<string, string>();
|
const map = new Map<string, string>();
|
||||||
for (const item of registrationEventList.value) {
|
for (const item of eventList.value) {
|
||||||
if (item.id) {
|
if (item.id) {
|
||||||
map.set(item.id, item.name || "未命名事件");
|
map.set(item.id, item.name || "未命名事件");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
const selectedRegistrationEventIdSet = computed(() => new Set(selectedRegistrationEventIdList.value));
|
const selectedEventIdSet = computed(() => new Set(selectedEventIdList.value));
|
||||||
const filteredMonthRecords = computed(() => {
|
const filteredMonthRecords = computed(() => {
|
||||||
if (!selectedRegistrationEventIdList.value.length) {
|
if (!selectedEventIdList.value.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return monthRecords.value.filter(item => {
|
return monthRecords.value.filter(item => {
|
||||||
if (!item.registrationEventId || !selectedRegistrationEventIdSet.value.has(item.registrationEventId)) {
|
if (!item.eventId || !selectedEventIdSet.value.has(item.eventId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const recordDate = Time.toDateObject(item.registeredAt);
|
const recordDate = Time.toDateObject(item.registeredAt);
|
||||||
@@ -276,7 +276,7 @@ const summaryList = computed<SummaryItem[]>(() => [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "最近登记",
|
label: "最近登记",
|
||||||
value: getRecordRegistrationEventName(filteredMonthRecords.value[0]) || "暂无"
|
value: getRecordEventName(filteredMonthRecords.value[0]) || "暂无"
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const chartList = computed<ChartProfile[]>(() => {
|
const chartList = computed<ChartProfile[]>(() => {
|
||||||
@@ -303,25 +303,25 @@ const chartList = computed<ChartProfile[]>(() => {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
const rankDateFilterLabel = computed(() => rankDateOptions.find(item => item.value === rankDateFilterType.value)?.label || "全部");
|
const rankDateFilterLabel = computed(() => rankDateOptions.find(item => item.value === rankDateFilterType.value)?.label || "全部");
|
||||||
const rankRegistrationEventOptions = computed(() => registrationEventList.value.map(item => ({
|
const rankEventOptions = computed(() => eventList.value.map(item => ({
|
||||||
label: item.name || "未命名事件",
|
label: item.name || "未命名事件",
|
||||||
value: item.id || ""
|
value: item.id || ""
|
||||||
})).filter(item => item.value));
|
})).filter(item => item.value));
|
||||||
const rankRegistrationEventFilterLabel = computed(() => {
|
const rankEventFilterLabel = computed(() => {
|
||||||
if (!rankRegistrationEventIdList.value.length || rankRegistrationEventIdList.value.length === registrationEventList.value.length) {
|
if (!rankEventIdList.value.length || rankEventIdList.value.length === eventList.value.length) {
|
||||||
return "全部事件";
|
return "全部事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
return registrationEventList.value
|
return eventList.value
|
||||||
.filter(item => item.id && rankRegistrationEventIdList.value.includes(item.id))
|
.filter(item => item.id && rankEventIdList.value.includes(item.id))
|
||||||
.map(item => item.name || "未命名事件")
|
.map(item => item.name || "未命名事件")
|
||||||
.join("、");
|
.join("、");
|
||||||
});
|
});
|
||||||
const rankSummaryText = computed(() => {
|
const rankSummaryText = computed(() => {
|
||||||
if (rankDateFilterType.value === "custom") {
|
if (rankDateFilterType.value === "custom") {
|
||||||
return `${rankDateRange.value[0] || "-"} ~ ${rankDateRange.value[1] || "-"}|${rankRegistrationEventFilterLabel.value}`;
|
return `${rankDateRange.value[0] || "-"} ~ ${rankDateRange.value[1] || "-"}|${rankEventFilterLabel.value}`;
|
||||||
}
|
}
|
||||||
return `范围:${rankDateFilterLabel.value}|${rankRegistrationEventFilterLabel.value}`;
|
return `范围:${rankDateFilterLabel.value}|${rankEventFilterLabel.value}`;
|
||||||
});
|
});
|
||||||
const visibleCustomerRegistrationRankList = computed(() => {
|
const visibleCustomerRegistrationRankList = computed(() => {
|
||||||
return customerRegistrationRankList.value.slice(0, rankPageIndex.value * rankPageSize);
|
return customerRegistrationRankList.value.slice(0, rankPageIndex.value * rankPageSize);
|
||||||
@@ -409,9 +409,9 @@ function resizeCharts(): void {
|
|||||||
// ---------- 数据 ----------
|
// ---------- 数据 ----------
|
||||||
|
|
||||||
async function loadActions(): Promise<void> {
|
async function loadActions(): Promise<void> {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
selectedRegistrationEventIdList.value = registrationEventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
selectedEventIdList.value = eventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
||||||
rankRegistrationEventIdList.value = selectedRegistrationEventIdList.value;
|
rankEventIdList.value = selectedEventIdList.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadCustomer(code: string): Promise<void> {
|
async function loadCustomer(code: string): Promise<void> {
|
||||||
@@ -485,10 +485,10 @@ async function loadMonthRecords(): Promise<void> {
|
|||||||
const endDate = getDateEnd(addDays(beginDate, 41));
|
const endDate = getDateEnd(addDays(beginDate, 41));
|
||||||
calendarLoading.value = true;
|
calendarLoading.value = true;
|
||||||
try {
|
try {
|
||||||
monthRecords.value = await RegistrationEventRecordAPI.rangeList({
|
monthRecords.value = await EventRecordAPI.rangeList({
|
||||||
customerId,
|
customerId,
|
||||||
beginRegisteredAt: Time.toTimestamp(beginDate),
|
beginAt: Time.toTimestamp(beginDate),
|
||||||
endRegisteredAt: Time.toTimestamp(endDate)
|
endAt: Time.toTimestamp(endDate)
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
calendarLoading.value = false;
|
calendarLoading.value = false;
|
||||||
@@ -514,7 +514,7 @@ async function loadCustomerRegistrationRank(force = false): Promise<void> {
|
|||||||
rankLoading.value = true;
|
rankLoading.value = true;
|
||||||
let request = force ? undefined : rankPendingRequestMap.get(cacheKey);
|
let request = force ? undefined : rankPendingRequestMap.get(cacheKey);
|
||||||
if (!request) {
|
if (!request) {
|
||||||
request = RegistrationEventRecordAPI.customerRegistrationRank(buildRankStatReq());
|
request = EventRecordAPI.customerRegistrationRank(buildRankStatReq());
|
||||||
rankPendingRequestMap.set(cacheKey, request);
|
rankPendingRequestMap.set(cacheKey, request);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -619,27 +619,27 @@ function handleScanStatusChange(value: string): void {
|
|||||||
scanStatus.value = value;
|
scanStatus.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordDateText(record: RegistrationEventRecord): string {
|
function getRecordDateText(record: EventRecord): string {
|
||||||
const date = Time.toDateObject(record.registeredAt);
|
const date = Time.toDateObject(record.registeredAt);
|
||||||
return date ? Time.formatDate(date) : "";
|
return date ? Time.formatDate(date) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordRegistrationEventName(record?: RegistrationEventRecord): string {
|
function getRecordEventName(record?: EventRecord): string {
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return record.registrationEvent?.name || (record.registrationEventId ? registrationEventNameMap.value.get(record.registrationEventId) : "") || "未知事件";
|
return record.event?.name || (record.eventId ? eventNameMap.value.get(record.eventId) : "") || "未知事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRankCustomerPhotoURL(item: CustomerRegistrationRankView): string {
|
function getRankCustomerPhotoURL(item: CustomerEventRankView): string {
|
||||||
if (!item.customerPhotoAttachmentId) {
|
if (!item.customerPhotoAttachmentId) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return AttachmentAPI.getReadURL(item.customerPhotoAttachmentId);
|
return AttachmentAPI.getReadURL(item.customerPhotoAttachmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRankCustomerInitial(item: CustomerRegistrationRankView): string {
|
function getRankCustomerInitial(item: CustomerEventRankView): string {
|
||||||
return (item.customerName || "客").slice(0, 1);
|
return (item.customerName || "客").slice(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,7 +708,7 @@ function buildBehaviorStructureOption(): EChartsOption {
|
|||||||
...buildBaseOption(),
|
...buildBaseOption(),
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
data: registrationEventList.value.filter(item => item.id && selectedRegistrationEventIdSet.value.has(item.id)).map(item => item.name || "未命名事件"),
|
data: eventList.value.filter(item => item.id && selectedEventIdSet.value.has(item.id)).map(item => item.name || "未命名事件"),
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
@@ -720,12 +720,12 @@ function buildBehaviorStructureOption(): EChartsOption {
|
|||||||
{
|
{
|
||||||
name: "登记次数",
|
name: "登记次数",
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: registrationEventList.value
|
data: eventList.value
|
||||||
.filter(item => item.id && selectedRegistrationEventIdSet.value.has(item.id))
|
.filter(item => item.id && selectedEventIdSet.value.has(item.id))
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
value: filteredMonthRecords.value.filter(record => record.registrationEventId === item.id).length,
|
value: filteredMonthRecords.value.filter(record => record.eventId === item.id).length,
|
||||||
itemStyle: getRegistrationEventColor(item) ? {
|
itemStyle: getEventColor(item) ? {
|
||||||
color: getRegistrationEventColor(item)
|
color: getEventColor(item)
|
||||||
} : undefined
|
} : undefined
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -733,8 +733,8 @@ function buildBehaviorStructureOption(): EChartsOption {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRegistrationEventColor(registrationEvent: RegistrationEvent): string | undefined {
|
function getEventColor(event: EventItem): string | undefined {
|
||||||
return registrationEvent.color || undefined;
|
return event.color || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRelationOption(): EChartsOption {
|
function buildRelationOption(): EChartsOption {
|
||||||
@@ -795,27 +795,27 @@ function getDateEnd(date: Date): Date {
|
|||||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRankStatReq(): Partial<RegistrationEventRecordRangeReq> | undefined {
|
function buildRankStatReq(): Partial<EventRecordPage> | undefined {
|
||||||
const selectedAllEvent = !rankRegistrationEventIdList.value.length || rankRegistrationEventIdList.value.length === registrationEventList.value.length;
|
const selectedAllEvent = !rankEventIdList.value.length || rankEventIdList.value.length === eventList.value.length;
|
||||||
if (rankDateFilterType.value === "all") {
|
if (rankDateFilterType.value === "all") {
|
||||||
return selectedAllEvent ? undefined : {
|
return selectedAllEvent ? undefined : {
|
||||||
registrationEventIdList: rankRegistrationEventIdList.value
|
eventIdList: rankEventIdList.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
beginRegisteredAt: Time.toTimestamp(parseRankDateTime(rankDateRange.value[0], false)),
|
beginAt: Time.toTimestamp(parseRankDateTime(rankDateRange.value[0], false)),
|
||||||
endRegisteredAt: Time.toTimestamp(parseRankDateTime(rankDateRange.value[1], true)),
|
endAt: Time.toTimestamp(parseRankDateTime(rankDateRange.value[1], true)),
|
||||||
registrationEventIdList: selectedAllEvent ? undefined : rankRegistrationEventIdList.value
|
eventIdList: selectedAllEvent ? undefined : rankEventIdList.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRankCacheKey(): string {
|
function buildRankCacheKey(): string {
|
||||||
const req = buildRankStatReq();
|
const req = buildRankStatReq();
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
beginRegisteredAt: req?.beginRegisteredAt,
|
beginAt: req?.beginAt,
|
||||||
endRegisteredAt: req?.endRegisteredAt,
|
endAt: req?.endAt,
|
||||||
registrationEventIdList: req?.registrationEventIdList?.slice().sort()
|
eventIdList: req?.eventIdList?.slice().sort()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,3 +1135,4 @@ function parseRankDateTime(value: string, isEnd: boolean): Date {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.customer.read" class="customer-detail">
|
<div v-can="permissionCodes.customer.read" class="customer-detail">
|
||||||
<customer-profile-card
|
<customer-profile-card
|
||||||
class="customer"
|
class="customer"
|
||||||
:customer="customer"
|
:customer="customer"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<div class="invite">
|
<div class="invite">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">邀请客户</div>
|
<div class="title">邀请客户</div>
|
||||||
<div v-can="gaoPermissionCodes.customer.update" class="actions">
|
<div v-can="permissionCodes.customer.update" class="actions">
|
||||||
<t-button
|
<t-button
|
||||||
size="extra-small"
|
size="extra-small"
|
||||||
content="选择添加"
|
content="选择添加"
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
<template #right>
|
<template #right>
|
||||||
<div v-can="gaoPermissionCodes.customer.update" class="remove bg-red white" @click="removeInvitee(item)">删除</div>
|
<div v-can="permissionCodes.customer.update" class="remove bg-red white" @click="removeInvitee(item)">删除</div>
|
||||||
</template>
|
</template>
|
||||||
</t-swipe-cell>
|
</t-swipe-cell>
|
||||||
<div v-if="!invitees.length" class="empty">暂无邀请客户</div>
|
<div v-if="!invitees.length" class="empty">暂无邀请客户</div>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="bottom-actions">
|
<div class="bottom-actions">
|
||||||
<t-button
|
<t-button
|
||||||
v-can="gaoPermissionCodes.customer.delete"
|
v-can="permissionCodes.customer.delete"
|
||||||
class="delete bg-white"
|
class="delete bg-white"
|
||||||
theme="danger"
|
theme="danger"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
>
|
>
|
||||||
删除客户
|
删除客户
|
||||||
</t-button>
|
</t-button>
|
||||||
<t-button v-can="gaoPermissionCodes.customer.update" theme="primary" class="edit" @click="toEdit">编辑客户</t-button>
|
<t-button v-can="permissionCodes.customer.update" theme="primary" class="edit" @click="toEdit">编辑客户</t-button>
|
||||||
</div>
|
</div>
|
||||||
<customer-selector
|
<customer-selector
|
||||||
v-model="selectedInviteeId"
|
v-model="selectedInviteeId"
|
||||||
@@ -159,7 +159,7 @@ import CustomerSelector from "@/components/CustomerSelector.vue";
|
|||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
import type { BodyAreaValue, Customer } from "@/types/Customer";
|
import type { BodyAreaValue, Customer } from "@/types/Customer";
|
||||||
import { getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
|
import { getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { LunarCalendar } from "@/utils/LunarCalendar";
|
import { LunarCalendar } from "@/utils/LunarCalendar";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -183,7 +183,7 @@ const showSolarBirthdate = ref(false);
|
|||||||
let latestDecodedText = "";
|
let latestDecodedText = "";
|
||||||
|
|
||||||
const inviter = computed(() => customer.value?.introducerCustomer);
|
const inviter = computed(() => customer.value?.introducerCustomer);
|
||||||
const canReadStat = computed(() => userStore.hasPermissions(gaoPermissionCodes.stat.read));
|
const canReadStat = computed(() => userStore.hasPermissions(permissionCodes.stat.read));
|
||||||
const creatorName = computed(() => {
|
const creatorName = computed(() => {
|
||||||
const creatorUser = customer.value?.creatorUser;
|
const creatorUser = customer.value?.creatorUser;
|
||||||
return creatorUser?.nick || creatorUser?.name || customer.value?.creatorUserId || "无";
|
return creatorUser?.nick || creatorUser?.name || customer.value?.creatorUserId || "无";
|
||||||
@@ -627,3 +627,4 @@ watch(() => route.params.id, async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ import PhotoField from "@/components/PhotoField.vue";
|
|||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
import type { BodyAreaValue, Customer, CustomerBirthdateCalendar, CustomerGender } from "@/types/Customer";
|
import type { BodyAreaValue, Customer, CustomerBirthdateCalendar, CustomerGender } from "@/types/Customer";
|
||||||
import { createBodyAreaValue, getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
|
import { createBodyAreaValue, getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { compressUploadImage } from "@/utils/imageCompression";
|
import { compressUploadImage } from "@/utils/imageCompression";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -179,7 +179,7 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const isCreateMode = computed(() => route.name === "CustomerCreate");
|
const isCreateMode = computed(() => route.name === "CustomerCreate");
|
||||||
const savePermissionCode = computed(() => isCreateMode.value ? gaoPermissionCodes.customer.create : gaoPermissionCodes.customer.update);
|
const savePermissionCode = computed(() => isCreateMode.value ? permissionCodes.customer.create : permissionCodes.customer.update);
|
||||||
const selectedAreas = ref<BodyAreaValue>(createBodyAreaValue());
|
const selectedAreas = ref<BodyAreaValue>(createBodyAreaValue());
|
||||||
const selectorVisible = ref(false);
|
const selectorVisible = ref(false);
|
||||||
const referrerCustomerId = ref("");
|
const referrerCustomerId = ref("");
|
||||||
@@ -583,3 +583,4 @@ watch(() => route.fullPath, async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="pageRef" v-can="gaoPermissionCodes.customer.read" class="customer-list">
|
<div ref="pageRef" v-can="permissionCodes.customer.read" class="customer-list">
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<t-input
|
<t-input
|
||||||
v-model.trim="keyword"
|
v-model.trim="keyword"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
</section>
|
</section>
|
||||||
<div class="bottom-actions">
|
<div class="bottom-actions">
|
||||||
<t-button
|
<t-button
|
||||||
v-can="gaoPermissionCodes.customer.export"
|
v-can="permissionCodes.customer.export"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="export bg-white"
|
class="export bg-white"
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
导出
|
导出
|
||||||
</t-button>
|
</t-button>
|
||||||
<t-button
|
<t-button
|
||||||
v-can="gaoPermissionCodes.customer.create"
|
v-can="permissionCodes.customer.create"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
class="create"
|
class="create"
|
||||||
@click="router.push('/management/customers/create')"
|
@click="router.push('/management/customers/create')"
|
||||||
@@ -86,7 +86,7 @@ import CustomerAPI from "@/api/CustomerAPI";
|
|||||||
import GenderIcon from "@/components/GenderIcon.vue";
|
import GenderIcon from "@/components/GenderIcon.vue";
|
||||||
import type { Customer, CustomerSearchReq } from "@/types/Customer";
|
import type { Customer, CustomerSearchReq } from "@/types/Customer";
|
||||||
import { getCustomerPhotoURL } from "@/utils/customer";
|
import { getCustomerPhotoURL } from "@/utils/customer";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CustomerList"
|
name: "CustomerList"
|
||||||
@@ -372,3 +372,4 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
+30
-29
@@ -97,7 +97,7 @@
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
:loading="removing"
|
:loading="removing"
|
||||||
:disabled="saving || removing"
|
:disabled="saving || removing"
|
||||||
@click="confirmRemoveRegistrationEvent"
|
@click="confirmRemoveEventItem"
|
||||||
>
|
>
|
||||||
删除事件
|
删除事件
|
||||||
</t-button>
|
</t-button>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
theme="primary"
|
theme="primary"
|
||||||
:loading="saving"
|
:loading="saving"
|
||||||
:disabled="removing"
|
:disabled="removing"
|
||||||
@click="saveRegistrationEvent"
|
@click="saveEventItem"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</t-button>
|
</t-button>
|
||||||
@@ -184,28 +184,28 @@ import { Time } from "timi-web";
|
|||||||
import { DialogPlugin, Toast } from "tdesign-mobile-vue";
|
import { DialogPlugin, Toast } from "tdesign-mobile-vue";
|
||||||
import type { ColorObject } from "tdesign-mobile-vue";
|
import type { ColorObject } from "tdesign-mobile-vue";
|
||||||
import type { Role } from "timi-web";
|
import type { Role } from "timi-web";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import type { RegistrationEvent, RegistrationEventStatus, RegistrationLimitType } from "@/types/RegistrationEvent";
|
import type { EventItem, EventStatus, EventLimitType } from "@/types/EventItem";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
type LimitOption = {
|
type LimitOption = {
|
||||||
label: string;
|
label: string;
|
||||||
value: RegistrationLimitType;
|
value: EventLimitType;
|
||||||
};
|
};
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventEdit"
|
name: "EventEdit"
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const routeId = computed(() => String(route.params.id || ""));
|
const routeId = computed(() => String(route.params.id || ""));
|
||||||
const isCreateMode = computed(() => !routeId.value);
|
const isCreateMode = computed(() => !routeId.value);
|
||||||
const savePermissionCode = computed(() => isCreateMode.value ? gaoPermissionCodes.registrationEvent.create : gaoPermissionCodes.registrationEvent.update);
|
const savePermissionCode = computed(() => isCreateMode.value ? permissionCodes.event.create : permissionCodes.event.update);
|
||||||
const deletePermissionCode = gaoPermissionCodes.registrationEvent.delete;
|
const deletePermissionCode = permissionCodes.event.delete;
|
||||||
const pageAccess = computed(() => ({
|
const pageAccess = computed(() => ({
|
||||||
value: [gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read, savePermissionCode.value],
|
value: [permissionCodes.event.read, permissionCodes.user.read, savePermissionCode.value],
|
||||||
mode: "and" as const
|
mode: "and" as const
|
||||||
}));
|
}));
|
||||||
const roleOptions = ref<Role[]>([]);
|
const roleOptions = ref<Role[]>([]);
|
||||||
@@ -239,7 +239,7 @@ const limitOptions: LimitOption[] = [
|
|||||||
{ label: "事件总次数", value: "TOTAL_LIMIT" },
|
{ label: "事件总次数", value: "TOTAL_LIMIT" },
|
||||||
{ label: "单客户总次数", value: "CUSTOMER_TOTAL_LIMIT" }
|
{ label: "单客户总次数", value: "CUSTOMER_TOTAL_LIMIT" }
|
||||||
];
|
];
|
||||||
const statusOptions: Array<{ label: string; value: RegistrationEventStatus }> = [
|
const statusOptions: Array<{ label: string; value: EventStatus }> = [
|
||||||
{ label: "草稿", value: "DRAFT" },
|
{ label: "草稿", value: "DRAFT" },
|
||||||
{ label: "可登记", value: "ACTIVE" },
|
{ label: "可登记", value: "ACTIVE" },
|
||||||
{ label: "不可登记", value: "INACTIVE" }
|
{ label: "不可登记", value: "INACTIVE" }
|
||||||
@@ -251,9 +251,9 @@ const formModel = reactive({
|
|||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
color: "#0f766e",
|
color: "#0f766e",
|
||||||
status: "ACTIVE" as RegistrationEventStatus,
|
status: "ACTIVE" as EventStatus,
|
||||||
requirePhoto: false,
|
requirePhoto: false,
|
||||||
limitType: "NONE" as RegistrationLimitType,
|
limitType: "NONE" as EventLimitType,
|
||||||
limitValueText: "",
|
limitValueText: "",
|
||||||
beginAtText: "",
|
beginAtText: "",
|
||||||
endAtText: "",
|
endAtText: "",
|
||||||
@@ -262,7 +262,7 @@ const formModel = reactive({
|
|||||||
const limitPickerVisible = ref(false);
|
const limitPickerVisible = ref(false);
|
||||||
const limitPickerDraft = ref<string[]>([formModel.limitType]);
|
const limitPickerDraft = ref<string[]>([formModel.limitType]);
|
||||||
const statusPickerVisible = ref(false);
|
const statusPickerVisible = ref(false);
|
||||||
const statusPickerDraft = ref<RegistrationEventStatus[]>([formModel.status]);
|
const statusPickerDraft = ref<EventStatus[]>([formModel.status]);
|
||||||
const minimumDateTimeText = "1970-01-01 00:00";
|
const minimumDateTimeText = "1970-01-01 00:00";
|
||||||
const nowDateTimeText = computed(() => Time.formatDateTime(new Date()));
|
const nowDateTimeText = computed(() => Time.formatDateTime(new Date()));
|
||||||
const endAtMinimumDateTimeText = computed(() => formModel.beginAtText || minimumDateTimeText);
|
const endAtMinimumDateTimeText = computed(() => formModel.beginAtText || minimumDateTimeText);
|
||||||
@@ -304,11 +304,11 @@ function updateColorDraftFromPalette(context: { color: ColorObject }): void {
|
|||||||
colorDraft.value = context.color.hex8;
|
colorDraft.value = context.color.hex8;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLimitText(value: RegistrationLimitType): string {
|
function getLimitText(value: EventLimitType): string {
|
||||||
return limitOptions.find(option => option.value === value)?.label || "不限";
|
return limitOptions.find(option => option.value === value)?.label || "不限";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusText(value: RegistrationEventStatus): string {
|
function getStatusText(value: EventStatus): string {
|
||||||
return statusOptions.find(option => option.value === value)?.label || "可登记";
|
return statusOptions.find(option => option.value === value)?.label || "可登记";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ function openLimitPicker(): void {
|
|||||||
function confirmLimitPicker(value: Array<string | number>): void {
|
function confirmLimitPicker(value: Array<string | number>): void {
|
||||||
const limitType = value[0];
|
const limitType = value[0];
|
||||||
if (typeof limitType === "string") {
|
if (typeof limitType === "string") {
|
||||||
formModel.limitType = limitType as RegistrationLimitType;
|
formModel.limitType = limitType as EventLimitType;
|
||||||
}
|
}
|
||||||
limitPickerVisible.value = false;
|
limitPickerVisible.value = false;
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ function confirmStatusPicker(value: Array<string | number>): void {
|
|||||||
statusPickerVisible.value = false;
|
statusPickerVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValueLimitType(value: RegistrationLimitType): boolean {
|
function isValueLimitType(value: EventLimitType): boolean {
|
||||||
return value === "INTERVAL_HOUR" || value === "TOTAL_LIMIT" || value === "CUSTOMER_TOTAL_LIMIT";
|
return value === "INTERVAL_HOUR" || value === "TOTAL_LIMIT" || value === "CUSTOMER_TOTAL_LIMIT";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +383,7 @@ function validateForm(): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPayload(): RegistrationEvent {
|
function buildPayload(): EventItem {
|
||||||
return {
|
return {
|
||||||
id: formModel.id || undefined,
|
id: formModel.id || undefined,
|
||||||
name: formModel.name.trim(),
|
name: formModel.name.trim(),
|
||||||
@@ -431,7 +431,7 @@ function clearEndAt(): void {
|
|||||||
endAtPickerVisible.value = false;
|
endAtPickerVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveRegistrationEvent(): Promise<void> {
|
async function saveEventItem(): Promise<void> {
|
||||||
if (!validateForm()) {
|
if (!validateForm()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -440,9 +440,9 @@ async function saveRegistrationEvent(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const payload = buildPayload();
|
const payload = buildPayload();
|
||||||
if (isCreateMode.value) {
|
if (isCreateMode.value) {
|
||||||
await RegistrationEventAPI.create(payload);
|
await EventAPI.create(payload);
|
||||||
} else {
|
} else {
|
||||||
await RegistrationEventAPI.update(payload);
|
await EventAPI.update(payload);
|
||||||
}
|
}
|
||||||
showToast("登记事件已保存");
|
showToast("登记事件已保存");
|
||||||
await router.replace("/management/registration-events");
|
await router.replace("/management/registration-events");
|
||||||
@@ -453,7 +453,7 @@ async function saveRegistrationEvent(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmRemoveRegistrationEvent(): Promise<void> {
|
async function confirmRemoveEventItem(): Promise<void> {
|
||||||
const id = formModel.id || routeId.value;
|
const id = formModel.id || routeId.value;
|
||||||
if (!id || removing.value) {
|
if (!id || removing.value) {
|
||||||
return;
|
return;
|
||||||
@@ -470,19 +470,19 @@ async function confirmRemoveRegistrationEvent(): Promise<void> {
|
|||||||
},
|
},
|
||||||
async onConfirm() {
|
async onConfirm() {
|
||||||
dialog.destroy();
|
dialog.destroy();
|
||||||
await removeRegistrationEvent(id);
|
await removeEventItem(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeRegistrationEvent(id: string): Promise<void> {
|
async function removeEventItem(id: string): Promise<void> {
|
||||||
if (removing.value) {
|
if (removing.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
removing.value = true;
|
removing.value = true;
|
||||||
try {
|
try {
|
||||||
await RegistrationEventAPI.remove(id);
|
await EventAPI.remove(id);
|
||||||
showToast("登记事件已删除");
|
showToast("登记事件已删除");
|
||||||
await router.replace("/management/registration-events");
|
await router.replace("/management/registration-events");
|
||||||
} catch {
|
} catch {
|
||||||
@@ -501,12 +501,12 @@ function showToast(message: string, theme: "success" | "error" | "warning" = "su
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fillForm(): Promise<void> {
|
async function fillForm(): Promise<void> {
|
||||||
roleOptions.value = await GaoUserAPI.roleList();
|
roleOptions.value = await ManagedUserAPI.roleList();
|
||||||
if (isCreateMode.value) {
|
if (isCreateMode.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const current = await RegistrationEventAPI.detail(routeId.value);
|
const current = await EventAPI.detail(routeId.value);
|
||||||
Object.assign(formModel, {
|
Object.assign(formModel, {
|
||||||
id: current.id || "",
|
id: current.id || "",
|
||||||
name: current.name || "",
|
name: current.name || "",
|
||||||
@@ -696,3 +696,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
+28
-27
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="{ value: [gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read], mode: 'and' }" class="registration-event-list">
|
<div v-can="{ value: [permissionCodes.event.read, permissionCodes.user.read], mode: 'and' }" class="registration-event-list">
|
||||||
<t-cell-group theme="card">
|
<t-cell-group theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
class="item"
|
class="item"
|
||||||
v-for="item in registrationEventList"
|
v-for="item in eventList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:arrow="canUpdateRegistrationEvent"
|
:arrow="canUpdateEventItem"
|
||||||
:hover="canUpdateRegistrationEvent"
|
:hover="canUpdateEventItem"
|
||||||
@click="handleRegistrationEventClick(item)"
|
@click="handleEventClick(item)"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</t-cell>
|
</t-cell>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<div v-if="!registrationEventList.length" class="empty-block">暂无登记事件</div>
|
<div v-if="!eventList.length" class="empty-block">暂无登记事件</div>
|
||||||
<t-button v-can="gaoPermissionCodes.registrationEvent.create" class="create" theme="primary" @click="createRegistrationEvent">
|
<t-button v-can="permissionCodes.event.create" class="create" theme="primary" @click="createEventItem">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<t-icon name="add" />
|
<t-icon name="add" />
|
||||||
</template>
|
</template>
|
||||||
@@ -53,30 +53,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { useUserStore, type Role } from "timi-web";
|
import { useUserStore, type Role } from "timi-web";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { RegistrationEvent, RegistrationEventStatus, RegistrationLimitType } from "@/types/RegistrationEvent";
|
import type { EventItem, EventStatus, EventLimitType } from "@/types/EventItem";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventList"
|
name: "eventList"
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const navBarStore = useNavBarStore();
|
const navBarStore = useNavBarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const defaultColor = "#2f88ff";
|
const defaultColor = "#2f88ff";
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const roleOptions = ref<Role[]>([]);
|
const roleOptions = ref<Role[]>([]);
|
||||||
const canUpdateRegistrationEvent = computed(() => userStore.hasPermissions(gaoPermissionCodes.registrationEvent.update));
|
const canUpdateEventItem = computed(() => userStore.hasPermissions(permissionCodes.event.update));
|
||||||
|
|
||||||
type StatusTag = {
|
type StatusTag = {
|
||||||
text: string;
|
text: string;
|
||||||
theme: "default" | "primary" | "danger";
|
theme: "default" | "primary" | "danger";
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixedStatusTagMap: Partial<Record<RegistrationEventStatus, StatusTag>> = {
|
const fixedStatusTagMap: Partial<Record<EventStatus, StatusTag>> = {
|
||||||
DRAFT: { text: "草稿", theme: "default" },
|
DRAFT: { text: "草稿", theme: "default" },
|
||||||
INACTIVE: { text: "不可登记", theme: "default" },
|
INACTIVE: { text: "不可登记", theme: "default" },
|
||||||
DELETED: { text: "已删除", theme: "default" }
|
DELETED: { text: "已删除", theme: "default" }
|
||||||
@@ -85,7 +85,7 @@ const activeStatusTag: StatusTag = { text: "可登记", theme: "primary" };
|
|||||||
const notEffectiveStatusTag: StatusTag = { text: "未生效", theme: "default" };
|
const notEffectiveStatusTag: StatusTag = { text: "未生效", theme: "default" };
|
||||||
const expiredStatusTag: StatusTag = { text: "已过期", theme: "danger" };
|
const expiredStatusTag: StatusTag = { text: "已过期", theme: "danger" };
|
||||||
|
|
||||||
function createRegistrationEvent(): void {
|
function createEventItem(): void {
|
||||||
router.push("/management/registration-events/create");
|
router.push("/management/registration-events/create");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ async function goSort(): Promise<void> {
|
|||||||
await router.push("/management/registration-events/sort");
|
await router.push("/management/registration-events/sort");
|
||||||
}
|
}
|
||||||
|
|
||||||
function editRegistrationEvent(item: RegistrationEvent): void {
|
function editEventItem(item: EventItem): void {
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,15 +101,15 @@ function editRegistrationEvent(item: RegistrationEvent): void {
|
|||||||
router.push(`/management/registration-events/${item.id}/edit`);
|
router.push(`/management/registration-events/${item.id}/edit`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRegistrationEventClick(item: RegistrationEvent): void {
|
function handleEventClick(item: EventItem): void {
|
||||||
if (!canUpdateRegistrationEvent.value) {
|
if (!canUpdateEventItem.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
editRegistrationEvent(item);
|
editEventItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusTag(item: RegistrationEvent): StatusTag {
|
function getStatusTag(item: EventItem): StatusTag {
|
||||||
const status = item.status || "ACTIVE";
|
const status = item.status || "ACTIVE";
|
||||||
const fixedStatusTag = fixedStatusTagMap[status];
|
const fixedStatusTag = fixedStatusTagMap[status];
|
||||||
if (fixedStatusTag) {
|
if (fixedStatusTag) {
|
||||||
@@ -125,8 +125,8 @@ function getStatusTag(item: RegistrationEvent): StatusTag {
|
|||||||
return activeStatusTag;
|
return activeStatusTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLimitText(item: RegistrationEvent): string {
|
function getLimitText(item: EventItem): string {
|
||||||
const limitTextMap: Record<RegistrationLimitType, string> = {
|
const limitTextMap: Record<EventLimitType, string> = {
|
||||||
NONE: "不限次数",
|
NONE: "不限次数",
|
||||||
DAILY_HALF_DAY: "上午/下午各一次",
|
DAILY_HALF_DAY: "上午/下午各一次",
|
||||||
INTERVAL_HOUR: `每隔 ${item.limitValue || 0} 小时`,
|
INTERVAL_HOUR: `每隔 ${item.limitValue || 0} 小时`,
|
||||||
@@ -145,19 +145,19 @@ function getRoleName(code: string): string {
|
|||||||
|
|
||||||
async function loadData(): Promise<void> {
|
async function loadData(): Promise<void> {
|
||||||
const [eventResult, roles] = await Promise.all([
|
const [eventResult, roles] = await Promise.all([
|
||||||
RegistrationEventAPI.list({
|
EventAPI.list({
|
||||||
index: 0,
|
index: 0,
|
||||||
size: 100
|
size: 100
|
||||||
}),
|
}),
|
||||||
GaoUserAPI.roleList()
|
ManagedUserAPI.roleList()
|
||||||
]);
|
]);
|
||||||
roleOptions.value = roles || [];
|
roleOptions.value = roles || [];
|
||||||
registrationEventList.value = eventResult.list || [];
|
eventList.value = eventResult.list || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
navBarStore.clearRight();
|
navBarStore.clearRight();
|
||||||
if (canUpdateRegistrationEvent.value) {
|
if (canUpdateEventItem.value) {
|
||||||
navBarStore.setRightText("排序", "var(--td-brand-color)", goSort);
|
navBarStore.setRightText("排序", "var(--td-brand-color)", goSort);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -245,3 +245,4 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
+12
-11
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.registrationEventRecord.read" class="registration-record-detail">
|
<div v-can="permissionCodes.eventRecord.read" class="registration-record-detail">
|
||||||
<customer-profile-card v-if="record?.customer" class="customer-profile" :customer="record.customer" />
|
<customer-profile-card v-if="record?.customer" class="customer-profile" :customer="record.customer" />
|
||||||
<t-cell-group v-if="record" title="登记信息" theme="card">
|
<t-cell-group v-if="record" title="登记信息" theme="card">
|
||||||
<t-cell title="登记事件" :note="record.registrationEvent?.name || '未知事件'" />
|
<t-cell title="登记事件" :note="record.event?.name || '未知事件'" />
|
||||||
<t-cell title="登记时间" :note="formatTime(record.registeredAt)" />
|
<t-cell title="登记时间" :note="formatTime(record.registeredAt)" />
|
||||||
<t-cell title="登记人" :note="Text.display(record.operator?.nick || record.operator?.name)" />
|
<t-cell title="登记人" :note="Text.display(record.operator?.nick || record.operator?.name)" />
|
||||||
<t-cell title="客户编码" :note="Text.display(record.customer?.code)" />
|
<t-cell title="客户编码" :note="Text.display(record.customer?.code)" />
|
||||||
@@ -22,28 +22,28 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Text, Time, useUserStore } from "timi-web";
|
import { Text, Time, useUserStore } from "timi-web";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { RegistrationEventRecord } from "@/types/RegistrationEventRecord";
|
import type { EventRecord } from "@/types/EventRecord";
|
||||||
import { getRegistrationEventRecordPhotoURL } from "@/utils/registrationEventRecord";
|
import { getEventRecordPhotoURL } from "@/utils/eventRecord";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventRecordDetail"
|
name: "EventRecordDetail"
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const navBarStore = useNavBarStore();
|
const navBarStore = useNavBarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const record = ref<RegistrationEventRecord>();
|
const record = ref<EventRecord>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const canUpdateRecord = computed(() => userStore.hasPermissions(gaoPermissionCodes.registrationEventRecord.update));
|
const canUpdateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||||
const recordPhotoList = computed(() => {
|
const recordPhotoList = computed(() => {
|
||||||
const url = getRegistrationEventRecordPhotoURL(record.value);
|
const url = getEventRecordPhotoURL(record.value);
|
||||||
return url ? [url] : [];
|
return url ? [url] : [];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ async function loadRecord(): Promise<void> {
|
|||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
record.value = await RegistrationEventRecordAPI.detail(recordId);
|
record.value = await EventRecordAPI.detail(recordId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
record.value = undefined;
|
record.value = undefined;
|
||||||
console.warn("登记事件记录加载失败", err);
|
console.warn("登记事件记录加载失败", err);
|
||||||
@@ -132,3 +132,4 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
+43
-42
@@ -43,10 +43,10 @@
|
|||||||
class="customer-profile"
|
class="customer-profile"
|
||||||
:customer="selectedCustomer"
|
:customer="selectedCustomer"
|
||||||
/>
|
/>
|
||||||
<registration-event-selector
|
<event-selector
|
||||||
v-model="selectedRegistrationEventIdList"
|
v-model="selectedEventIdList"
|
||||||
class="registration-event-selector"
|
class="registration-event-selector"
|
||||||
:registration-event-list="registrationEventList"
|
:event-list="eventList"
|
||||||
required-mark
|
required-mark
|
||||||
/>
|
/>
|
||||||
<t-form-item label="登记时间" content-align="right">
|
<t-form-item label="登记时间" content-align="right">
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<t-cell
|
<t-cell
|
||||||
v-for="item in recentRecordList"
|
v-for="item in recentRecordList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:title="getRecordRegistrationEventName(item)"
|
:title="getRecordEventName(item)"
|
||||||
:description="formatTime(item.registeredAt)"
|
:description="formatTime(item.registeredAt)"
|
||||||
arrow
|
arrow
|
||||||
hover
|
hover
|
||||||
@@ -154,24 +154,24 @@
|
|||||||
import { AttachmentAPI, Time, type Attachment } from "timi-web";
|
import { AttachmentAPI, Time, type Attachment } from "timi-web";
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import CodeScanner from "@/components/CodeScanner.vue";
|
import CodeScanner from "@/components/CodeScanner.vue";
|
||||||
import CustomerList from "@/components/CustomerList.vue";
|
import CustomerList from "@/components/CustomerList.vue";
|
||||||
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||||
import RegistrationEventSelector from "@/components/RegistrationEventSelector.vue";
|
import EventSelector from "@/components/EventSelector.vue";
|
||||||
import { useSettingStore } from "@/store/settingStore";
|
import { useSettingStore } from "@/store/settingStore";
|
||||||
import type { Customer } from "@/types/Customer";
|
import type { Customer } from "@/types/Customer";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import type { RegistrationEventRecord } from "@/types/RegistrationEventRecord";
|
import type { EventRecord } from "@/types/EventRecord";
|
||||||
import { getRegistrationEventRecordPhotoURL } from "@/utils/registrationEventRecord";
|
import { getEventRecordPhotoURL } from "@/utils/eventRecord";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { compressUploadImageList } from "@/utils/imageCompression";
|
import { compressUploadImageList } from "@/utils/imageCompression";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventRecordEdit"
|
name: "EventRecordEdit"
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -183,9 +183,9 @@ const selectorVisible = ref(false);
|
|||||||
const selectedCustomerListVisible = ref(false);
|
const selectedCustomerListVisible = ref(false);
|
||||||
const selectedCustomerIdList = ref<string[]>([]);
|
const selectedCustomerIdList = ref<string[]>([]);
|
||||||
const selectedCustomerList = ref<Customer[]>([]);
|
const selectedCustomerList = ref<Customer[]>([]);
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedRegistrationEventIdList = ref<string[]>([]);
|
const selectedEventIdList = ref<string[]>([]);
|
||||||
const recentRecordList = ref<RegistrationEventRecord[]>([]);
|
const recentRecordList = ref<EventRecord[]>([]);
|
||||||
const minimumDateTimeText = "1970-01-01 00:00";
|
const minimumDateTimeText = "1970-01-01 00:00";
|
||||||
const registeredAtText = ref("");
|
const registeredAtText = ref("");
|
||||||
const registeredAtDraftText = ref("");
|
const registeredAtDraftText = ref("");
|
||||||
@@ -197,7 +197,7 @@ const photoUploading = ref(false);
|
|||||||
const scannerVisible = ref(false);
|
const scannerVisible = ref(false);
|
||||||
const scanStatus = ref("点击扫码开始识别。");
|
const scanStatus = ref("点击扫码开始识别。");
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const editingRecord = ref<RegistrationEventRecord>();
|
const editingRecord = ref<EventRecord>();
|
||||||
let latestDecodedText = "";
|
let latestDecodedText = "";
|
||||||
|
|
||||||
type EditablePhoto = {
|
type EditablePhoto = {
|
||||||
@@ -208,11 +208,11 @@ type EditablePhoto = {
|
|||||||
|
|
||||||
const routeId = computed(() => String(route.params.id || ""));
|
const routeId = computed(() => String(route.params.id || ""));
|
||||||
const isEditMode = computed(() => !!routeId.value);
|
const isEditMode = computed(() => !!routeId.value);
|
||||||
const pageAccess = computed(() => isEditMode.value ? gaoPermissionCodes.registrationEventRecord.update : gaoPermissionCodes.registrationEventRecord.create);
|
const pageAccess = computed(() => isEditMode.value ? permissionCodes.eventRecord.update : permissionCodes.eventRecord.create);
|
||||||
const saveButtonText = computed(() => isEditMode.value ? "保存记录" : "提交");
|
const saveButtonText = computed(() => isEditMode.value ? "保存记录" : "提交");
|
||||||
const selectedCustomer = computed(() => selectedCustomerList.value[0]);
|
const selectedCustomer = computed(() => selectedCustomerList.value[0]);
|
||||||
const showSelectedCustomerProfile = computed(() => selectedCustomerList.value.length === 1 && !!selectedCustomer.value?.id);
|
const showSelectedCustomerProfile = computed(() => selectedCustomerList.value.length === 1 && !!selectedCustomer.value?.id);
|
||||||
const canSubmit = computed(() => selectedCustomerList.value.some(item => !!item.id) && 0 < selectedRegistrationEventIdList.value.length);
|
const canSubmit = computed(() => selectedCustomerList.value.some(item => !!item.id) && 0 < selectedEventIdList.value.length);
|
||||||
const customerDisplayText = computed(() => {
|
const customerDisplayText = computed(() => {
|
||||||
if (1 < selectedCustomerList.value.length) {
|
if (1 < selectedCustomerList.value.length) {
|
||||||
return `已选择 ${selectedCustomerList.value.length} 位客户`;
|
return `已选择 ${selectedCustomerList.value.length} 位客户`;
|
||||||
@@ -229,9 +229,9 @@ const photoList = computed<string[]>({
|
|||||||
photoItemList.value = photoItemList.value.filter(item => urlSet.has(item.url));
|
photoItemList.value = photoItemList.value.filter(item => urlSet.has(item.url));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const registrationEventNameMap = computed(() => {
|
const eventNameMap = computed(() => {
|
||||||
const map = new Map<string, string>();
|
const map = new Map<string, string>();
|
||||||
for (const item of registrationEventList.value) {
|
for (const item of eventList.value) {
|
||||||
if (item.id) {
|
if (item.id) {
|
||||||
map.set(item.id, item.name || "未命名事件");
|
map.set(item.id, item.name || "未命名事件");
|
||||||
}
|
}
|
||||||
@@ -242,23 +242,23 @@ const registrationEventNameMap = computed(() => {
|
|||||||
// ---------- 页面数据 ----------
|
// ---------- 页面数据 ----------
|
||||||
|
|
||||||
async function loadPageData(): Promise<void> {
|
async function loadPageData(): Promise<void> {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
if (isEditMode.value) {
|
if (isEditMode.value) {
|
||||||
await loadRecordForEdit();
|
await loadRecordForEdit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedRegistrationEventIdList.value = registrationEventList.value.slice(0, 1).map(item => item.id).filter((item): item is string => Boolean(item));
|
selectedEventIdList.value = eventList.value.slice(0, 1).map(item => item.id).filter((item): item is string => Boolean(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRecordForEdit(): Promise<void> {
|
async function loadRecordForEdit(): Promise<void> {
|
||||||
const record = await RegistrationEventRecordAPI.detail(routeId.value);
|
const record = await EventRecordAPI.detail(routeId.value);
|
||||||
editingRecord.value = record;
|
editingRecord.value = record;
|
||||||
appendRecordRegistrationEvent(record);
|
appendRecordEvent(record);
|
||||||
selectedCustomerList.value = record.customer ? [record.customer] : [];
|
selectedCustomerList.value = record.customer ? [record.customer] : [];
|
||||||
customerCode.value = record.customer?.code || record.customerId || "";
|
customerCode.value = record.customer?.code || record.customerId || "";
|
||||||
selectedCustomerIdList.value = record.customer?.id ? [record.customer.id] : [];
|
selectedCustomerIdList.value = record.customer?.id ? [record.customer.id] : [];
|
||||||
selectedRegistrationEventIdList.value = record.registrationEventId ? [record.registrationEventId] : [];
|
selectedEventIdList.value = record.eventId ? [record.eventId] : [];
|
||||||
registeredAtText.value = record.registeredAt ? Time.formatDateTime(new Date(record.registeredAt)) : "";
|
registeredAtText.value = record.registeredAt ? Time.formatDateTime(new Date(record.registeredAt)) : "";
|
||||||
note.value = record.remark || "";
|
note.value = record.remark || "";
|
||||||
photoItemList.value = (record.attachmentList || [])
|
photoItemList.value = (record.attachmentList || [])
|
||||||
@@ -271,12 +271,12 @@ async function loadRecordForEdit(): Promise<void> {
|
|||||||
await loadRecentRecordList();
|
await loadRecentRecordList();
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendRecordRegistrationEvent(record: RegistrationEventRecord): void {
|
function appendRecordEvent(record: EventRecord): void {
|
||||||
if (!record.registrationEvent?.id || registrationEventList.value.some(item => item.id === record.registrationEvent?.id)) {
|
if (!record.event?.id || eventList.value.some(item => item.id === record.event?.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
registrationEventList.value = [record.registrationEvent, ...registrationEventList.value];
|
eventList.value = [record.event, ...eventList.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 扫码 ----------
|
// ---------- 扫码 ----------
|
||||||
@@ -394,22 +394,22 @@ async function loadRecentRecordList(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endRegisteredAt = Time.toTimestamp(new Date());
|
const endAt = Time.toTimestamp(new Date());
|
||||||
recentRecordList.value = (await RegistrationEventRecordAPI.rangeList({
|
recentRecordList.value = (await EventRecordAPI.rangeList({
|
||||||
customerId,
|
customerId,
|
||||||
beginRegisteredAt: 0,
|
beginAt: 0,
|
||||||
endRegisteredAt
|
endAt
|
||||||
}))
|
}))
|
||||||
.sort((left, right) => (right.registeredAt || 0) - (left.registeredAt || 0))
|
.sort((left, right) => (right.registeredAt || 0) - (left.registeredAt || 0))
|
||||||
.slice(0, 5);
|
.slice(0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordRegistrationEventName(record: RegistrationEventRecord): string {
|
function getRecordEventName(record: EventRecord): string {
|
||||||
return record.registrationEvent?.name || (record.registrationEventId ? registrationEventNameMap.value.get(record.registrationEventId) : "") || "未知事件";
|
return record.event?.name || (record.eventId ? eventNameMap.value.get(record.eventId) : "") || "未知事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordPhotoURL(record: RegistrationEventRecord): string {
|
function getRecordPhotoURL(record: EventRecord): string {
|
||||||
return getRegistrationEventRecordPhotoURL(record);
|
return getEventRecordPhotoURL(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 照片 ----------
|
// ---------- 照片 ----------
|
||||||
@@ -473,9 +473,9 @@ async function submitRecord(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const customerIdList = selectedCustomerList.value.map(customer => customer.id).filter((item): item is string => !!item);
|
const customerIdList = selectedCustomerList.value.map(customer => customer.id).filter((item): item is string => !!item);
|
||||||
for (const registrationEventId of selectedRegistrationEventIdList.value) {
|
for (const eventId of selectedEventIdList.value) {
|
||||||
await RegistrationEventRecordAPI.create({
|
await EventRecordAPI.create({
|
||||||
registrationEventId,
|
eventId,
|
||||||
customerIdList,
|
customerIdList,
|
||||||
remark: note.value || "客户现场登记",
|
remark: note.value || "客户现场登记",
|
||||||
registeredAt: buildRegisteredAt(),
|
registeredAt: buildRegisteredAt(),
|
||||||
@@ -500,7 +500,7 @@ function validateForm(): boolean {
|
|||||||
showToast("请先选择或扫码客户", "warning");
|
showToast("请先选择或扫码客户", "warning");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!selectedRegistrationEventIdList.value.length) {
|
if (!selectedEventIdList.value.length) {
|
||||||
showToast("请至少选择一个登记事件", "warning");
|
showToast("请至少选择一个登记事件", "warning");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -515,10 +515,10 @@ async function updateRecord(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await RegistrationEventRecordAPI.update({
|
await EventRecordAPI.update({
|
||||||
...editingRecord.value,
|
...editingRecord.value,
|
||||||
customerId: submitCustomer.id,
|
customerId: submitCustomer.id,
|
||||||
registrationEventId: selectedRegistrationEventIdList.value[0],
|
eventId: selectedEventIdList.value[0],
|
||||||
remark: note.value,
|
remark: note.value,
|
||||||
registeredAt: buildRegisteredAt(),
|
registeredAt: buildRegisteredAt(),
|
||||||
attachmentList: buildPhotoAttachmentList()
|
attachmentList: buildPhotoAttachmentList()
|
||||||
@@ -728,3 +728,4 @@ watch(customerCode, async (value) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
+37
-36
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="pageRef" v-can="gaoPermissionCodes.registrationEventRecord.read" class="registration-record-list">
|
<div ref="pageRef" v-can="permissionCodes.eventRecord.read" class="registration-record-list">
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<t-input
|
<t-input
|
||||||
v-model.trim="keyword"
|
v-model.trim="keyword"
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
>
|
>
|
||||||
<t-cell
|
<t-cell
|
||||||
class="cell"
|
class="cell"
|
||||||
:title="getRecordRegistrationEventName(item)"
|
:title="getRecordEventName(item)"
|
||||||
:description="getRecordCustomerName(item)"
|
:description="getRecordCustomerName(item)"
|
||||||
:note="formatTime(item.registeredAt)"
|
:note="formatTime(item.registeredAt)"
|
||||||
arrow
|
arrow
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<t-image
|
<t-image
|
||||||
v-if="getRecordPhotoURL(item)"
|
v-if="getRecordPhotoURL(item)"
|
||||||
:src="getRecordPhotoURL(item)"
|
:src="getRecordPhotoURL(item)"
|
||||||
:alt="getRecordRegistrationEventName(item)"
|
:alt="getRecordEventName(item)"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
class="photo"
|
class="photo"
|
||||||
/>
|
/>
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
<div v-if="recordList.length" class="load-state" v-text="loadStateText" />
|
<div v-if="recordList.length" class="load-state" v-text="loadStateText" />
|
||||||
</div>
|
</div>
|
||||||
<t-button
|
<t-button
|
||||||
v-can="gaoPermissionCodes.registrationEventRecord.export"
|
v-can="permissionCodes.eventRecord.export"
|
||||||
class="export"
|
class="export"
|
||||||
theme="primary"
|
theme="primary"
|
||||||
:loading="exporting"
|
:loading="exporting"
|
||||||
@@ -125,21 +125,21 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DialogPlugin, Toast, type DropdownValue } from "tdesign-mobile-vue";
|
import { DialogPlugin, Toast, type DropdownValue } from "tdesign-mobile-vue";
|
||||||
import { AttachmentAPI, Scroller, Time, Toolkit, useUserStore } from "timi-web";
|
import { AttachmentAPI, Scroller, Time, Toolkit, useUserStore } from "timi-web";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import type { RegistrationEventRecordListItemView, RegistrationEventRecordRangeReq } from "@/types/RegistrationEventRecord";
|
import type { EventRecordListItemView, EventRecordPage } from "@/types/EventRecord";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
type DateFilterType = "today" | "yesterday" | "beforeYesterday" | "lastWeek" | "all" | "custom";
|
type DateFilterType = "today" | "yesterday" | "beforeYesterday" | "lastWeek" | "all" | "custom";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventRecordList"
|
name: "EventRecordList"
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageSize = 20;
|
const pageSize = 20;
|
||||||
const scrollListenerName = "RegistrationEventRecordList";
|
const scrollListenerName = "EventRecordList";
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const navBarStore = useNavBarStore();
|
const navBarStore = useNavBarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@@ -158,7 +158,7 @@ const keyword = ref("");
|
|||||||
const dateFilterType = ref<DateFilterType>("today");
|
const dateFilterType = ref<DateFilterType>("today");
|
||||||
const dateRange = ref(buildPresetRange("today"));
|
const dateRange = ref(buildPresetRange("today"));
|
||||||
const actionFilter = ref<string[]>([]);
|
const actionFilter = ref<string[]>([]);
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const startVisible = ref(false);
|
const startVisible = ref(false);
|
||||||
const endVisible = ref(false);
|
const endVisible = ref(false);
|
||||||
const pageRef = ref<HTMLElement>();
|
const pageRef = ref<HTMLElement>();
|
||||||
@@ -169,25 +169,25 @@ function getPageScrollElement(): HTMLElement | null {
|
|||||||
return pageRef.value?.closest(".pages") || null;
|
return pageRef.value?.closest(".pages") || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canCreateRecord = computed(() => userStore.hasPermissions(gaoPermissionCodes.registrationEventRecord.create));
|
const canCreateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.create));
|
||||||
const isCustomRange = computed(() => dateFilterType.value === "custom");
|
const isCustomRange = computed(() => dateFilterType.value === "custom");
|
||||||
const dateFilterLabel = computed(() => dateOptions.find(item => item.value === dateFilterType.value)?.label || "今天");
|
const dateFilterLabel = computed(() => dateOptions.find(item => item.value === dateFilterType.value)?.label || "今天");
|
||||||
const actionOptions = computed(() => registrationEventList.value.map(item => ({
|
const actionOptions = computed(() => eventList.value.map(item => ({
|
||||||
label: item.name || "未命名事件",
|
label: item.name || "未命名事件",
|
||||||
value: item.id || ""
|
value: item.id || ""
|
||||||
})).filter(item => item.value));
|
})).filter(item => item.value));
|
||||||
const eventFilterLabel = computed(() => {
|
const eventFilterLabel = computed(() => {
|
||||||
if (!actionFilter.value.length || actionFilter.value.length === registrationEventList.value.length) {
|
if (!actionFilter.value.length || actionFilter.value.length === eventList.value.length) {
|
||||||
return "全部事件";
|
return "全部事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
return registrationEventList.value
|
return eventList.value
|
||||||
.filter(item => item.id && actionFilter.value.includes(item.id))
|
.filter(item => item.id && actionFilter.value.includes(item.id))
|
||||||
.map(item => item.name || "未命名事件")
|
.map(item => item.name || "未命名事件")
|
||||||
.join("、");
|
.join("、");
|
||||||
});
|
});
|
||||||
const summaryBehaviorText = computed(() => `事件:${eventFilterLabel.value}`);
|
const summaryBehaviorText = computed(() => `事件:${eventFilterLabel.value}`);
|
||||||
const canDeleteRecord = computed(() => userStore.hasPermissions(gaoPermissionCodes.registrationEventRecord.delete));
|
const canDeleteRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||||
const summaryText = computed(() => {
|
const summaryText = computed(() => {
|
||||||
if (!isCustomRange.value) {
|
if (!isCustomRange.value) {
|
||||||
const current = dateOptions.find(item => item.value === dateFilterType.value);
|
const current = dateOptions.find(item => item.value === dateFilterType.value);
|
||||||
@@ -231,7 +231,7 @@ watch(dateRange, async (value) => {
|
|||||||
|
|
||||||
const pageIndex = ref(0);
|
const pageIndex = ref(0);
|
||||||
const totalCount = ref(0);
|
const totalCount = ref(0);
|
||||||
const recordList = ref<RegistrationEventRecordListItemView[]>([]);
|
const recordList = ref<EventRecordListItemView[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const noMore = ref(false);
|
const noMore = ref(false);
|
||||||
let loadToken = 0;
|
let loadToken = 0;
|
||||||
@@ -260,7 +260,7 @@ async function loadRecords(): Promise<void> {
|
|||||||
loadToken = token;
|
loadToken = token;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await RegistrationEventRecordAPI.rangePage({
|
const result = await EventRecordAPI.rangePage({
|
||||||
index: pageIndex.value,
|
index: pageIndex.value,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
equalsExample: buildRangeReq()
|
equalsExample: buildRangeReq()
|
||||||
@@ -285,8 +285,8 @@ const exporting = ref(false);
|
|||||||
const deletingId = ref("");
|
const deletingId = ref("");
|
||||||
|
|
||||||
async function loadActions(): Promise<void> {
|
async function loadActions(): Promise<void> {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
actionFilter.value = registrationEventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
actionFilter.value = eventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectDateFilter(type: DropdownValue): Promise<void> {
|
async function selectDateFilter(type: DropdownValue): Promise<void> {
|
||||||
@@ -307,7 +307,7 @@ async function exportExcel(): Promise<void> {
|
|||||||
}
|
}
|
||||||
exporting.value = true;
|
exporting.value = true;
|
||||||
try {
|
try {
|
||||||
const blob = await RegistrationEventRecordAPI.rangeExport(buildRangeReq());
|
const blob = await EventRecordAPI.rangeExport(buildRangeReq());
|
||||||
Toolkit.downloadBlob(blob, `登记事件记录-${Time.formatDate(new Date())}.xlsx`);
|
Toolkit.downloadBlob(blob, `登记事件记录-${Time.formatDate(new Date())}.xlsx`);
|
||||||
showToast("导出已开始");
|
showToast("导出已开始");
|
||||||
} finally {
|
} finally {
|
||||||
@@ -326,7 +326,7 @@ async function goCreate(): Promise<void> {
|
|||||||
await router.push("/management/registration-event-records/create");
|
await router.push("/management/registration-event-records/create");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirmRemoveRecord(record: RegistrationEventRecordListItemView): Promise<void> {
|
async function confirmRemoveRecord(record: EventRecordListItemView): Promise<void> {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -334,7 +334,7 @@ async function confirmRemoveRecord(record: RegistrationEventRecordListItemView):
|
|||||||
const recordId = record.id;
|
const recordId = record.id;
|
||||||
const dialog = DialogPlugin.confirm({
|
const dialog = DialogPlugin.confirm({
|
||||||
title: "删除登记记录",
|
title: "删除登记记录",
|
||||||
content: `确定删除 ${getRecordCustomerName(record)} 的 ${getRecordRegistrationEventName(record)} 记录吗?删除后无法恢复。`,
|
content: `确定删除 ${getRecordCustomerName(record)} 的 ${getRecordEventName(record)} 记录吗?删除后无法恢复。`,
|
||||||
cancelBtn: "取消",
|
cancelBtn: "取消",
|
||||||
confirmBtn: "删除",
|
confirmBtn: "删除",
|
||||||
onCancel() {
|
onCancel() {
|
||||||
@@ -354,7 +354,7 @@ async function removeRecord(id: string): Promise<void> {
|
|||||||
|
|
||||||
deletingId.value = id;
|
deletingId.value = id;
|
||||||
try {
|
try {
|
||||||
await RegistrationEventRecordAPI.remove(id);
|
await EventRecordAPI.remove(id);
|
||||||
recordList.value = recordList.value.filter(item => item.id !== id);
|
recordList.value = recordList.value.filter(item => item.id !== id);
|
||||||
totalCount.value = Math.max(0, totalCount.value - 1);
|
totalCount.value = Math.max(0, totalCount.value - 1);
|
||||||
showToast("登记记录已删除");
|
showToast("登记记录已删除");
|
||||||
@@ -365,22 +365,22 @@ async function removeRecord(id: string): Promise<void> {
|
|||||||
|
|
||||||
// ---------- 格式化 ----------
|
// ---------- 格式化 ----------
|
||||||
|
|
||||||
function getRecordRegistrationEventName(record: RegistrationEventRecordListItemView): string {
|
function getRecordEventName(record: EventRecordListItemView): string {
|
||||||
return record.registrationEventName || "未知事件";
|
return record.eventName || "未知事件";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordCustomerName(record: RegistrationEventRecordListItemView): string {
|
function getRecordCustomerName(record: EventRecordListItemView): string {
|
||||||
return record.customerName || "未知客户";
|
return record.customerName || "未知客户";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordPhotoURL(record: RegistrationEventRecordListItemView): string {
|
function getRecordPhotoURL(record: EventRecordListItemView): string {
|
||||||
if (!record.customerPhotoAttachmentId) {
|
if (!record.customerPhotoAttachmentId) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return AttachmentAPI.getReadURL(record.customerPhotoAttachmentId);
|
return AttachmentAPI.getReadURL(record.customerPhotoAttachmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecordCustomerInitial(record: RegistrationEventRecordListItemView): string {
|
function getRecordCustomerInitial(record: EventRecordListItemView): string {
|
||||||
return (record.customerName || "客").slice(0, 1);
|
return (record.customerName || "客").slice(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,14 +396,14 @@ function formatTime(value?: number): string {
|
|||||||
return Time.formatDateTime(date);
|
return Time.formatDateTime(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRangeReq(): RegistrationEventRecordRangeReq {
|
function buildRangeReq(): EventRecordPage {
|
||||||
const [beginRegisteredAt, endRegisteredAt] = buildUnixRange();
|
const [beginAt, endAt] = buildUnixRange();
|
||||||
const selectedAllEvent = !actionFilter.value.length || actionFilter.value.length === registrationEventList.value.length;
|
const selectedAllEvent = !actionFilter.value.length || actionFilter.value.length === eventList.value.length;
|
||||||
return {
|
return {
|
||||||
beginRegisteredAt,
|
beginAt,
|
||||||
endRegisteredAt,
|
endAt,
|
||||||
keyword: keyword.value.trim() || undefined,
|
keyword: keyword.value.trim() || undefined,
|
||||||
registrationEventIdList: selectedAllEvent ? undefined : actionFilter.value
|
eventIdList: selectedAllEvent ? undefined : actionFilter.value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,3 +648,4 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
+18
-17
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.registrationEvent.update" class="registration-event-sort">
|
<div v-can="permissionCodes.event.update" class="registration-event-sort">
|
||||||
<div class="tip" v-text="'按住右侧拖动手柄调整事件顺序,保存后生效'" />
|
<div class="tip" v-text="'按住右侧拖动手柄调整事件顺序,保存后生效'" />
|
||||||
<div ref="sortListRef" class="items">
|
<div ref="sortListRef" class="items">
|
||||||
<t-cell-group v-if="registrationEventList.length" theme="card">
|
<t-cell-group v-if="eventList.length" theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
class="cell"
|
class="cell"
|
||||||
:title="item.name || '未命名事件'"
|
:title="item.name || '未命名事件'"
|
||||||
v-for="(item, index) in registrationEventList"
|
v-for="(item, index) in eventList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:class="{ dragging: draggingIndex === index }"
|
:class="{ dragging: draggingIndex === index }"
|
||||||
>
|
>
|
||||||
@@ -55,18 +55,18 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast, type TagProps } from "tdesign-mobile-vue";
|
import { Toast, type TagProps } from "tdesign-mobile-vue";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { RegistrationEvent, RegistrationEventStatus } from "@/types/RegistrationEvent";
|
import type { EventItem, EventStatus } from "@/types/EventItem";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RegistrationEventSort"
|
name: "EventSort"
|
||||||
});
|
});
|
||||||
|
|
||||||
const navBarStore = useNavBarStore();
|
const navBarStore = useNavBarStore();
|
||||||
const defaultColor = "#2f88ff";
|
const defaultColor = "#2f88ff";
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const sortListRef = ref<HTMLElement>();
|
const sortListRef = ref<HTMLElement>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
@@ -78,7 +78,7 @@ let pointerMoved = false;
|
|||||||
let pointerTarget: HTMLElement | undefined;
|
let pointerTarget: HTMLElement | undefined;
|
||||||
let dragInputType: "pointer" | "touch" | undefined;
|
let dragInputType: "pointer" | "touch" | undefined;
|
||||||
|
|
||||||
function getStatusText(status?: RegistrationEventStatus): string {
|
function getStatusText(status?: EventStatus): string {
|
||||||
if (status === "DRAFT") {
|
if (status === "DRAFT") {
|
||||||
return "草稿";
|
return "草稿";
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ function getStatusText(status?: RegistrationEventStatus): string {
|
|||||||
return "可登记";
|
return "可登记";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusTheme(status?: RegistrationEventStatus): TagProps["theme"] {
|
function getStatusTheme(status?: EventStatus): TagProps["theme"] {
|
||||||
if (status === "DRAFT") {
|
if (status === "DRAFT") {
|
||||||
return "warning";
|
return "warning";
|
||||||
}
|
}
|
||||||
@@ -135,8 +135,8 @@ function moveDragAt(clientY: number): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [item] = registrationEventList.value.splice(draggingIndex.value, 1);
|
const [item] = eventList.value.splice(draggingIndex.value, 1);
|
||||||
registrationEventList.value.splice(targetIndex, 0, item);
|
eventList.value.splice(targetIndex, 0, item);
|
||||||
draggingIndex.value = targetIndex;
|
draggingIndex.value = targetIndex;
|
||||||
sortDirty.value = true;
|
sortDirty.value = true;
|
||||||
}
|
}
|
||||||
@@ -223,11 +223,11 @@ async function saveSort(): Promise<void> {
|
|||||||
|
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
try {
|
try {
|
||||||
const idList = registrationEventList.value
|
const idList = eventList.value
|
||||||
.map(item => item.id)
|
.map(item => item.id)
|
||||||
.filter((id): id is string => Boolean(id));
|
.filter((id): id is string => Boolean(id));
|
||||||
await RegistrationEventAPI.sort(idList);
|
await EventAPI.sort(idList);
|
||||||
registrationEventList.value.forEach((item, index) => {
|
eventList.value.forEach((item, index) => {
|
||||||
item.sort = index;
|
item.sort = index;
|
||||||
});
|
});
|
||||||
sortDirty.value = false;
|
sortDirty.value = false;
|
||||||
@@ -250,11 +250,11 @@ function showToast(message: string, theme: "success" | "error" = "success"): voi
|
|||||||
async function loadData(): Promise<void> {
|
async function loadData(): Promise<void> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await RegistrationEventAPI.list({
|
const result = await EventAPI.list({
|
||||||
index: 0,
|
index: 0,
|
||||||
size: 100
|
size: 100
|
||||||
});
|
});
|
||||||
registrationEventList.value = result.list || [];
|
eventList.value = result.list || [];
|
||||||
sortDirty.value = false;
|
sortDirty.value = false;
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -351,3 +351,4 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.stat.read" class="data-charts-page">
|
<div v-can="permissionCodes.stat.read" class="data-charts-page">
|
||||||
<section class="summary">
|
<section class="summary">
|
||||||
<div v-for="item in summaryList" :key="item.label" class="summary-item">
|
<div v-for="item in summaryList" :key="item.label" class="summary-item">
|
||||||
<span v-text="item.label" />
|
<span v-text="item.label" />
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<registration-event-record-calendar
|
<event-record-calendar
|
||||||
v-model="monthCursor"
|
v-model="monthCursor"
|
||||||
v-model:selected-registration-event-id-list="selectedRegistrationEventIdList"
|
v-model:selected-event-id-list="selectedEventIdList"
|
||||||
:registration-event-list="registrationEventList"
|
:event-list="eventList"
|
||||||
empty-text="当前月份暂无登记事件记录"
|
empty-text="当前月份暂无登记事件记录"
|
||||||
:loading="calendarLoading"
|
:loading="calendarLoading"
|
||||||
:records="monthRecords"
|
:records="monthRecords"
|
||||||
@@ -34,14 +34,14 @@ import { GridComponent, LegendComponent, TooltipComponent } from "echarts/compon
|
|||||||
import { init, use } from "echarts/core";
|
import { init, use } from "echarts/core";
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
import CustomerAPI from "@/api/CustomerAPI";
|
import CustomerAPI from "@/api/CustomerAPI";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import RegistrationEventRecordAPI from "@/api/RegistrationEventRecordAPI";
|
import EventRecordAPI from "@/api/EventRecordAPI";
|
||||||
import RegistrationEventRecordCalendar from "@/components/RegistrationEventRecordCalendar.vue";
|
import EventRecordCalendar from "@/components/EventRecordCalendar.vue";
|
||||||
import type { CustomerDailyStatView, CustomerGenderStatView } from "@/types/Customer";
|
import type { CustomerDailyStatView, CustomerGenderStatView } from "@/types/Customer";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import type { RegistrationEventRecord, RegistrationEventRecordCalendarView, RegistrationEventRecordDailyStatView } from "@/types/RegistrationEventRecord";
|
import type { EventRecord, EventRecordCalendarView, EventRecordDailyStatView } from "@/types/EventRecord";
|
||||||
import { Time } from "timi-web";
|
import { Time } from "timi-web";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
interface SummaryItem {
|
interface SummaryItem {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -69,12 +69,12 @@ use([
|
|||||||
|
|
||||||
const chartRefs = new Map<string, HTMLElement>();
|
const chartRefs = new Map<string, HTMLElement>();
|
||||||
const chartInstances = new Map<string, ECharts>();
|
const chartInstances = new Map<string, ECharts>();
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedRegistrationEventIdList = ref<string[]>([]);
|
const selectedEventIdList = ref<string[]>([]);
|
||||||
const customerDailyStatList = ref<CustomerDailyStatView[]>([]);
|
const customerDailyStatList = ref<CustomerDailyStatView[]>([]);
|
||||||
const customerGenderStatList = ref<CustomerGenderStatView[]>([]);
|
const customerGenderStatList = ref<CustomerGenderStatView[]>([]);
|
||||||
const recordDailyStatMap = ref<Record<string, RegistrationEventRecordDailyStatView[]>>({});
|
const recordDailyStatMap = ref<Record<string, EventRecordDailyStatView[]>>({});
|
||||||
const monthRecords = ref<RegistrationEventRecord[]>([]);
|
const monthRecords = ref<EventRecord[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const calendarLoading = ref(false);
|
const calendarLoading = ref(false);
|
||||||
const monthCursor = ref(getMonthStart(new Date()));
|
const monthCursor = ref(getMonthStart(new Date()));
|
||||||
@@ -87,8 +87,8 @@ const beginDate = getDateStart(addDays(new Date(), -6));
|
|||||||
const endDate = getDateEnd(new Date());
|
const endDate = getDateEnd(new Date());
|
||||||
const dateTextList = buildDateTextList(beginDate, endDate);
|
const dateTextList = buildDateTextList(beginDate, endDate);
|
||||||
const timestampRange = {
|
const timestampRange = {
|
||||||
beginRegisteredAt: Time.toTimestamp(beginDate),
|
beginAt: Time.toTimestamp(beginDate),
|
||||||
endRegisteredAt: Time.toTimestamp(endDate)
|
endAt: Time.toTimestamp(endDate)
|
||||||
};
|
};
|
||||||
|
|
||||||
const summaryList = computed<SummaryItem[]>(() => [
|
const summaryList = computed<SummaryItem[]>(() => [
|
||||||
@@ -188,27 +188,27 @@ function resizeCharts(): void {
|
|||||||
async function loadChartsData(): Promise<void> {
|
async function loadChartsData(): Promise<void> {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
registrationEventList.value = await RegistrationEventAPI.enabledList();
|
eventList.value = await EventAPI.enabledList();
|
||||||
const validRegistrationEventList = registrationEventList.value.filter((item): item is RegistrationEvent & { id: string } => Boolean(item.id));
|
const valideventList = eventList.value.filter((item): item is EventItem & { id: string } => Boolean(item.id));
|
||||||
selectedRegistrationEventIdList.value = validRegistrationEventList.map(item => item.id);
|
selectedEventIdList.value = valideventList.map(item => item.id);
|
||||||
const [customerStatList, genderStatList, recordDailyStatList] = await Promise.all([
|
const [customerStatList, genderStatList, recordDailyStatList] = await Promise.all([
|
||||||
CustomerAPI.dailyStat({
|
CustomerAPI.dailyStat({
|
||||||
beginCreatedAt: timestampRange.beginRegisteredAt,
|
beginCreatedAt: timestampRange.beginAt,
|
||||||
endCreatedAt: timestampRange.endRegisteredAt
|
endCreatedAt: timestampRange.endAt
|
||||||
}),
|
}),
|
||||||
CustomerAPI.genderStat(),
|
CustomerAPI.genderStat(),
|
||||||
Promise.all(validRegistrationEventList.map(async item => ({
|
Promise.all(valideventList.map(async item => ({
|
||||||
registrationEventId: item.id,
|
eventId: item.id,
|
||||||
stats: await RegistrationEventRecordAPI.allCustomerRegistrationEventDailyStat({
|
stats: await EventRecordAPI.allCustomerEventDailyStat({
|
||||||
registrationEventId: item.id,
|
eventId: item.id,
|
||||||
beginRegisteredAt: timestampRange.beginRegisteredAt,
|
beginAt: timestampRange.beginAt,
|
||||||
endRegisteredAt: timestampRange.endRegisteredAt
|
endAt: timestampRange.endAt
|
||||||
})
|
})
|
||||||
})))
|
})))
|
||||||
]);
|
]);
|
||||||
customerDailyStatList.value = customerStatList;
|
customerDailyStatList.value = customerStatList;
|
||||||
customerGenderStatList.value = genderStatList;
|
customerGenderStatList.value = genderStatList;
|
||||||
recordDailyStatMap.value = Object.fromEntries(recordDailyStatList.map(item => [item.registrationEventId, item.stats]));
|
recordDailyStatMap.value = Object.fromEntries(recordDailyStatList.map(item => [item.eventId, item.stats]));
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@@ -219,9 +219,9 @@ async function loadMonthRecords(): Promise<void> {
|
|||||||
const end = getDateEnd(addDays(begin, 41));
|
const end = getDateEnd(addDays(begin, 41));
|
||||||
calendarLoading.value = true;
|
calendarLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const calendarRecordList = await RegistrationEventRecordAPI.calendarRangeList({
|
const calendarRecordList = await EventRecordAPI.calendarRangeList({
|
||||||
beginRegisteredAt: Time.toTimestamp(begin),
|
beginAt: Time.toTimestamp(begin),
|
||||||
endRegisteredAt: Time.toTimestamp(end)
|
endAt: Time.toTimestamp(end)
|
||||||
});
|
});
|
||||||
monthRecords.value = calendarRecordList.map(buildCalendarRecord);
|
monthRecords.value = calendarRecordList.map(buildCalendarRecord);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -295,14 +295,14 @@ function buildCustomerGrowthOption(): EChartsOption {
|
|||||||
function buildRecordTrendOption(): EChartsOption {
|
function buildRecordTrendOption(): EChartsOption {
|
||||||
return {
|
return {
|
||||||
...buildBaseOption(),
|
...buildBaseOption(),
|
||||||
series: registrationEventList.value.map(action => ({
|
series: eventList.value.map(action => ({
|
||||||
name: action.name || "未命名事件",
|
name: action.name || "未命名事件",
|
||||||
type: "bar",
|
type: "bar",
|
||||||
stack: "record",
|
stack: "record",
|
||||||
itemStyle: getRegistrationEventColor(action) ? {
|
itemStyle: getEventColor(action) ? {
|
||||||
color: getRegistrationEventColor(action)
|
color: getEventColor(action)
|
||||||
} : undefined,
|
} : undefined,
|
||||||
data: dateTextList.map(dateText => countRecordByRegistrationEventAndDate(action.id || "", dateText))
|
data: dateTextList.map(dateText => countRecordByEventAndDate(action.id || "", dateText))
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ function buildEventStructureOption(): EChartsOption {
|
|||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
data: registrationEventList.value.map(item => item.name || "未命名事件"),
|
data: eventList.value.map(item => item.name || "未命名事件"),
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
@@ -326,10 +326,10 @@ function buildEventStructureOption(): EChartsOption {
|
|||||||
series: [{
|
series: [{
|
||||||
name: "登记次数",
|
name: "登记次数",
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: registrationEventList.value.map(item => ({
|
data: eventList.value.map(item => ({
|
||||||
value: countRecordByRegistrationEvent(item.id || ""),
|
value: countRecordByEvent(item.id || ""),
|
||||||
itemStyle: getRegistrationEventColor(item) ? {
|
itemStyle: getEventColor(item) ? {
|
||||||
color: getRegistrationEventColor(item)
|
color: getEventColor(item)
|
||||||
} : undefined
|
} : undefined
|
||||||
}))
|
}))
|
||||||
}]
|
}]
|
||||||
@@ -391,7 +391,7 @@ function sumCustomerNewCount(): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sumActionDailyCount(): number {
|
function sumActionDailyCount(): number {
|
||||||
return registrationEventList.value.reduce((sum, item) => sum + countRecordByRegistrationEvent(item.id || ""), 0);
|
return eventList.value.reduce((sum, item) => sum + countRecordByEvent(item.id || ""), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCustomerDailyStat(dateText: string): CustomerDailyStatView | undefined {
|
function getCustomerDailyStat(dateText: string): CustomerDailyStatView | undefined {
|
||||||
@@ -399,28 +399,28 @@ function getCustomerDailyStat(dateText: string): CustomerDailyStatView | undefin
|
|||||||
return customerDailyStatList.value.find(item => item.dateValue === dateValue);
|
return customerDailyStatList.value.find(item => item.dateValue === dateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function countRecordByRegistrationEventAndDate(registrationEventId: string, dateText: string): number {
|
function countRecordByEventAndDate(eventId: string, dateText: string): number {
|
||||||
const dateValue = Number(dateText.replaceAll("-", ""));
|
const dateValue = Number(dateText.replaceAll("-", ""));
|
||||||
return recordDailyStatMap.value[registrationEventId]?.find(item => item.dateValue === dateValue)?.dailyCount || 0;
|
return recordDailyStatMap.value[eventId]?.find(item => item.dateValue === dateValue)?.dailyCount || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function countRecordByRegistrationEvent(registrationEventId: string): number {
|
function countRecordByEvent(eventId: string): number {
|
||||||
return (recordDailyStatMap.value[registrationEventId] || []).reduce((sum, item) => sum + (item.dailyCount || 0), 0);
|
return (recordDailyStatMap.value[eventId] || []).reduce((sum, item) => sum + (item.dailyCount || 0), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRegistrationEventColor(registrationEvent: RegistrationEvent): string | undefined {
|
function getEventColor(event: EventItem): string | undefined {
|
||||||
return registrationEvent.color || undefined;
|
return event.color || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGenderCount(genderCode: "MALE" | "FEMALE" | "UNKNOWN"): number {
|
function getGenderCount(genderCode: "MALE" | "FEMALE" | "UNKNOWN"): number {
|
||||||
return customerGenderStatList.value.find(item => item.genderCode === genderCode)?.count || 0;
|
return customerGenderStatList.value.find(item => item.genderCode === genderCode)?.count || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCalendarRecord(record: RegistrationEventRecordCalendarView): RegistrationEventRecord {
|
function buildCalendarRecord(record: EventRecordCalendarView): EventRecord {
|
||||||
return {
|
return {
|
||||||
id: record.id,
|
id: record.id,
|
||||||
customerId: record.customerId,
|
customerId: record.customerId,
|
||||||
registrationEventId: record.registrationEventId,
|
eventId: record.eventId,
|
||||||
registeredAt: record.registeredAt,
|
registeredAt: record.registeredAt,
|
||||||
customer: record.customerId || record.customerCode || record.customerName ? {
|
customer: record.customerId || record.customerCode || record.customerName ? {
|
||||||
id: record.customerId,
|
id: record.customerId,
|
||||||
@@ -547,3 +547,4 @@ function buildDateTextList(begin: Date, end: Date): string[] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.qrCode.create" class="qr-page">
|
<div v-can="permissionCodes.qrCode.create" class="qr-page">
|
||||||
<t-form class="form" label-align="left">
|
<t-form class="form" label-align="left">
|
||||||
<t-form-item label="编码前缀">
|
<t-form-item label="编码前缀">
|
||||||
<t-input v-model.trim="formModel.prefix" placeholder="如 GAO" borderless />
|
<t-input v-model.trim="formModel.prefix" placeholder="如 GAO" borderless />
|
||||||
@@ -70,7 +70,7 @@ import {
|
|||||||
printManagementQrCodeImages,
|
printManagementQrCodeImages,
|
||||||
splitManagementQrCodePages
|
splitManagementQrCodePages
|
||||||
} from "@/utils/managementQrCode";
|
} from "@/utils/managementQrCode";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "ManagementQrCode"
|
name: "ManagementQrCode"
|
||||||
@@ -358,3 +358,4 @@ async function printQrCode(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { gaoModuleCode, usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { moduleCode, usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/gaoPermission";
|
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "PermissionEdit"
|
name: "PermissionEdit"
|
||||||
@@ -39,7 +39,7 @@ const pageAccess = computed(() => ({
|
|||||||
}));
|
}));
|
||||||
const formModel = reactive({
|
const formModel = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: "",
|
code: "",
|
||||||
name: "",
|
name: "",
|
||||||
description: ""
|
description: ""
|
||||||
@@ -50,7 +50,7 @@ async function fillForm(): Promise<void> {
|
|||||||
const current = await permissionStore.loadPermissionDetail(routeId.value);
|
const current = await permissionStore.loadPermissionDetail(routeId.value);
|
||||||
Object.assign(formModel, {
|
Object.assign(formModel, {
|
||||||
id: current.id,
|
id: current.id,
|
||||||
moduleCode: current.moduleCode || gaoModuleCode,
|
moduleCode: current.moduleCode || moduleCode,
|
||||||
code: current.code,
|
code: current.code,
|
||||||
name: current.name,
|
name: current.name,
|
||||||
description: current.description
|
description: current.description
|
||||||
@@ -60,7 +60,7 @@ async function fillForm(): Promise<void> {
|
|||||||
async function savePermission(): Promise<void> {
|
async function savePermission(): Promise<void> {
|
||||||
const permission = await permissionStore.upsertPermission({
|
const permission = await permissionStore.upsertPermission({
|
||||||
id: formModel.id,
|
id: formModel.id,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: formModel.code,
|
code: formModel.code,
|
||||||
name: formModel.name,
|
name: formModel.name,
|
||||||
description: formModel.description
|
description: formModel.description
|
||||||
@@ -98,3 +98,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from "@/store/permissionSettingsStore";
|
import type { Permission } from "@/store/permissionSettingsStore";
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/gaoPermission";
|
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "PermissionList"
|
name: "PermissionList"
|
||||||
@@ -113,3 +113,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes } from "@/utils/gaoPermission";
|
import { corePermissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "PermissionSettings"
|
name: "PermissionSettings"
|
||||||
@@ -112,3 +112,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { gaoModuleCode, usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { moduleCode, usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/gaoPermission";
|
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RoleEdit"
|
name: "RoleEdit"
|
||||||
@@ -57,7 +57,7 @@ const pageAccess = computed(() => ({
|
|||||||
}));
|
}));
|
||||||
const formModel = reactive({
|
const formModel = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: "",
|
code: "",
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -84,7 +84,7 @@ async function fillForm(): Promise<void> {
|
|||||||
const current = await permissionStore.loadRoleDetail(routeId.value);
|
const current = await permissionStore.loadRoleDetail(routeId.value);
|
||||||
Object.assign(formModel, {
|
Object.assign(formModel, {
|
||||||
id: current.id,
|
id: current.id,
|
||||||
moduleCode: current.moduleCode || gaoModuleCode,
|
moduleCode: current.moduleCode || moduleCode,
|
||||||
code: current.code,
|
code: current.code,
|
||||||
name: current.name,
|
name: current.name,
|
||||||
description: current.description,
|
description: current.description,
|
||||||
@@ -95,7 +95,7 @@ async function fillForm(): Promise<void> {
|
|||||||
async function saveRole(): Promise<void> {
|
async function saveRole(): Promise<void> {
|
||||||
const role = await permissionStore.upsertRole({
|
const role = await permissionStore.upsertRole({
|
||||||
id: formModel.id,
|
id: formModel.id,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: formModel.code,
|
code: formModel.code,
|
||||||
name: formModel.name,
|
name: formModel.name,
|
||||||
description: formModel.description,
|
description: formModel.description,
|
||||||
@@ -192,3 +192,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Role } from "@/store/permissionSettingsStore";
|
import type { Role } from "@/store/permissionSettingsStore";
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/gaoPermission";
|
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RoleList"
|
name: "RoleList"
|
||||||
@@ -87,3 +87,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import type { DropdownOption, DropdownValue } from "tdesign-mobile-vue";
|
|||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { useUserStore } from "timi-web";
|
import { useUserStore } from "timi-web";
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/gaoPermission";
|
import { corePermissionCodes, permissionManagementRoleAccess } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RolePermissionAssign"
|
name: "RolePermissionAssign"
|
||||||
@@ -288,3 +288,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.user.read" class="user-edit">
|
<div v-can="permissionCodes.user.read" class="user-edit">
|
||||||
<t-cell-group v-if="currentUser" theme="card">
|
<t-cell-group v-if="currentUser" theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
:title="currentUser.name"
|
:title="currentUser.name"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<t-cell-group v-if="currentUser" theme="card">
|
<t-cell-group v-if="currentUser" theme="card">
|
||||||
<t-cell
|
<t-cell
|
||||||
v-can="gaoPermissionCodes.user.update"
|
v-can="permissionCodes.user.update"
|
||||||
title="配置角色"
|
title="配置角色"
|
||||||
note="为用户授权业务角色"
|
note="为用户授权业务角色"
|
||||||
arrow
|
arrow
|
||||||
@@ -55,20 +55,20 @@
|
|||||||
<div v-if="!scopeList.length" class="empty-card">未配置</div>
|
<div v-if="!scopeList.length" class="empty-card">未配置</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<t-button v-can="gaoPermissionCodes.user.update" class="edit" theme="primary" @click="goEdit">编辑用户</t-button>
|
<t-button v-can="permissionCodes.user.update" class="edit" theme="primary" @click="goEdit">编辑用户</t-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { UserAPI, type User } from "timi-web";
|
import { UserAPI, type User } from "timi-web";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { buildEffectiveRoleCodeSet, getGaoRoleName } from "@/utils/gaoRole";
|
import { buildEffectiveRoleCodeSet, getRoleDisplayName } from "@/utils/role";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserDetail"
|
name: "UserDetail"
|
||||||
@@ -77,11 +77,11 @@ defineOptions({
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const permissionStore = usePermissionSettingsStore();
|
const permissionStore = usePermissionSettingsStore();
|
||||||
const gaoUser = ref<GaoUser>();
|
const managedUser = ref<ManagedUser>();
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
|
|
||||||
const currentUser = computed(() => {
|
const currentUser = computed(() => {
|
||||||
const item = gaoUser.value;
|
const item = managedUser.value;
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -89,19 +89,19 @@ const currentUser = computed(() => {
|
|||||||
return {
|
return {
|
||||||
id: item.id || "",
|
id: item.id || "",
|
||||||
name: user?.nick || user?.name || "未命名用户",
|
name: user?.nick || user?.name || "未命名用户",
|
||||||
role: getGaoRoleName(item.roleList?.[0], permissionStore.roleList),
|
role: getRoleDisplayName(item.roleList?.[0], permissionStore.roleList),
|
||||||
avatar: buildUserAvatar(user),
|
avatar: buildUserAvatar(user),
|
||||||
mobile: user?.phoneNo || "",
|
mobile: user?.phoneNo || "",
|
||||||
lastLoginAt: user?.lastLoginAt ? new Date(user.lastLoginAt).toLocaleString() : ""
|
lastLoginAt: user?.lastLoginAt ? new Date(user.lastLoginAt).toLocaleString() : ""
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const scopeList = computed(() => {
|
const scopeList = computed(() => {
|
||||||
const item = gaoUser.value;
|
const item = managedUser.value;
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return registrationEventList.value
|
return eventList.value
|
||||||
.filter(action => canRegistrationEvent(action, item))
|
.filter(action => canEventItem(action, item))
|
||||||
.map(item => item.name);
|
.map(item => item.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ function isUser(user?: Partial<User>): user is User {
|
|||||||
return typeof user?.name === "string";
|
return typeof user?.name === "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
function canRegistrationEvent(action: RegistrationEvent, item: GaoUser): boolean {
|
function canEventItem(action: EventItem, item: ManagedUser): boolean {
|
||||||
const userRoleCodeSet = buildEffectiveRoleCodeSet(item, permissionStore.roleList);
|
const userRoleCodeSet = buildEffectiveRoleCodeSet(item, permissionStore.roleList);
|
||||||
const roleCodeList = action.roleCodeList || [];
|
const roleCodeList = action.roleCodeList || [];
|
||||||
if (!roleCodeList.length) {
|
if (!roleCodeList.length) {
|
||||||
@@ -156,12 +156,12 @@ function canRegistrationEvent(action: RegistrationEvent, item: GaoUser): boolean
|
|||||||
async function loadData(): Promise<void> {
|
async function loadData(): Promise<void> {
|
||||||
const routeId = String(route.params.id || "");
|
const routeId = String(route.params.id || "");
|
||||||
const [detail, actions] = await Promise.all([
|
const [detail, actions] = await Promise.all([
|
||||||
GaoUserAPI.detail(routeId),
|
ManagedUserAPI.detail(routeId),
|
||||||
RegistrationEventAPI.enabledList(),
|
EventAPI.enabledList(),
|
||||||
permissionStore.loadAll()
|
permissionStore.loadAll()
|
||||||
]);
|
]);
|
||||||
gaoUser.value = detail;
|
managedUser.value = detail;
|
||||||
registrationEventList.value = actions || [];
|
eventList.value = actions || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -242,3 +242,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserEdit"
|
name: "UserEdit"
|
||||||
@@ -27,7 +27,7 @@ defineOptions({
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const currentGaoUser = ref<GaoUser>();
|
const currentManagedUser = ref<ManagedUser>();
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
const formModel = reactive({
|
const formModel = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
@@ -37,13 +37,13 @@ const formModel = reactive({
|
|||||||
password: ""
|
password: ""
|
||||||
});
|
});
|
||||||
const isCreateMode = computed(() => !String(route.params.id || ""));
|
const isCreateMode = computed(() => !String(route.params.id || ""));
|
||||||
const savePermissionCode = computed(() => isCreateMode.value ? gaoPermissionCodes.user.create : gaoPermissionCodes.user.update);
|
const savePermissionCode = computed(() => isCreateMode.value ? permissionCodes.user.create : permissionCodes.user.update);
|
||||||
|
|
||||||
async function fillForm(): Promise<void> {
|
async function fillForm(): Promise<void> {
|
||||||
const routeId = String(route.params.id || "");
|
const routeId = String(route.params.id || "");
|
||||||
if (routeId) {
|
if (routeId) {
|
||||||
const current = await GaoUserAPI.detail(routeId);
|
const current = await ManagedUserAPI.detail(routeId);
|
||||||
currentGaoUser.value = current;
|
currentManagedUser.value = current;
|
||||||
Object.assign(formModel, {
|
Object.assign(formModel, {
|
||||||
id: current.id,
|
id: current.id,
|
||||||
userId: current.userId || "",
|
userId: current.userId || "",
|
||||||
@@ -83,11 +83,11 @@ async function saveUser(): Promise<void> {
|
|||||||
|
|
||||||
isSaving.value = true;
|
isSaving.value = true;
|
||||||
try {
|
try {
|
||||||
const payload: GaoUser = {
|
const payload: ManagedUser = {
|
||||||
id: formModel.id || undefined,
|
id: formModel.id || undefined,
|
||||||
userId: formModel.userId || undefined,
|
userId: formModel.userId || undefined,
|
||||||
enabled: currentGaoUser.value?.enabled ?? true,
|
enabled: currentManagedUser.value?.enabled ?? true,
|
||||||
remark: currentGaoUser.value?.remark || "",
|
remark: currentManagedUser.value?.remark || "",
|
||||||
user: {
|
user: {
|
||||||
id: formModel.userId || undefined,
|
id: formModel.userId || undefined,
|
||||||
name: formModel.name,
|
name: formModel.name,
|
||||||
@@ -96,7 +96,7 @@ async function saveUser(): Promise<void> {
|
|||||||
password: isCreateMode.value ? formModel.password : undefined
|
password: isCreateMode.value ? formModel.password : undefined
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const saved = isCreateMode.value ? await GaoUserAPI.create(payload) : await GaoUserAPI.update(payload);
|
const saved = isCreateMode.value ? await ManagedUserAPI.create(payload) : await ManagedUserAPI.update(payload);
|
||||||
Toast({
|
Toast({
|
||||||
theme: "success",
|
theme: "success",
|
||||||
direction: "row",
|
direction: "row",
|
||||||
@@ -145,3 +145,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-can="gaoPermissionCodes.user.read" class="user-list">
|
<div v-can="permissionCodes.user.read" class="user-list">
|
||||||
<div v-for="item in userList" :key="item.id" class="item" @click="goDetail(item)">
|
<div v-for="item in userList" :key="item.id" class="item" @click="goDetail(item)">
|
||||||
<t-image :src="buildUserAvatar(item)" :alt="getUserName(item)" fit="cover" class="avatar" />
|
<t-image :src="buildUserAvatar(item)" :alt="getUserName(item)" fit="cover" class="avatar" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<span class="events gray" v-text="`可登记事件:${getActionScope(item)}`" />
|
<span class="events gray" v-text="`可登记事件:${getActionScope(item)}`" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<t-button v-can="gaoPermissionCodes.user.create" class="add" theme="primary" type="button" @click="goCreate">
|
<t-button v-can="permissionCodes.user.create" class="add" theme="primary" type="button" @click="goCreate">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<t-icon name="add" />
|
<t-icon name="add" />
|
||||||
</template>
|
</template>
|
||||||
@@ -22,13 +22,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { UserAPI, type User } from "timi-web";
|
import { UserAPI, type User } from "timi-web";
|
||||||
import RegistrationEventAPI from "@/api/RegistrationEventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
import { usePermissionSettingsStore } from "@/store/permissionSettingsStore";
|
||||||
import { buildEffectiveRoleCodeSet, getGaoRoleName } from "@/utils/gaoRole";
|
import { buildEffectiveRoleCodeSet, getRoleDisplayName } from "@/utils/role";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserList"
|
name: "UserList"
|
||||||
@@ -36,25 +36,25 @@ defineOptions({
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const permissionStore = usePermissionSettingsStore();
|
const permissionStore = usePermissionSettingsStore();
|
||||||
const userList = ref<GaoUser[]>([]);
|
const userList = ref<ManagedUser[]>([]);
|
||||||
const registrationEventList = ref<RegistrationEvent[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
|
|
||||||
function getUserName(item: GaoUser): string {
|
function getUserName(item: ManagedUser): string {
|
||||||
return item.user?.nick || item.user?.name || "未命名用户";
|
return item.user?.nick || item.user?.name || "未命名用户";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoleName(item: GaoUser): string {
|
function getRoleName(item: ManagedUser): string {
|
||||||
return getGaoRoleName(item.roleList?.[0], permissionStore.roleList);
|
return getRoleDisplayName(item.roleList?.[0], permissionStore.roleList);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActionScope(item: GaoUser): string {
|
function getActionScope(item: ManagedUser): string {
|
||||||
const nameList = registrationEventList.value
|
const nameList = eventList.value
|
||||||
.filter(action => canRegistrationEvent(action, item))
|
.filter(action => canEventItem(action, item))
|
||||||
.map(action => action.name);
|
.map(action => action.name);
|
||||||
return nameList.join("、") || "未配置";
|
return nameList.join("、") || "未配置";
|
||||||
}
|
}
|
||||||
|
|
||||||
function canRegistrationEvent(action: RegistrationEvent, item: GaoUser): boolean {
|
function canEventItem(action: EventItem, item: ManagedUser): boolean {
|
||||||
const userRoleCodeSet = buildEffectiveRoleCodeSet(item, permissionStore.roleList);
|
const userRoleCodeSet = buildEffectiveRoleCodeSet(item, permissionStore.roleList);
|
||||||
const roleCodeList = action.roleCodeList || [];
|
const roleCodeList = action.roleCodeList || [];
|
||||||
if (!roleCodeList.length) {
|
if (!roleCodeList.length) {
|
||||||
@@ -63,7 +63,7 @@ function canRegistrationEvent(action: RegistrationEvent, item: GaoUser): boolean
|
|||||||
return roleCodeList.some(code => userRoleCodeSet.has(code));
|
return roleCodeList.some(code => userRoleCodeSet.has(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildUserAvatar(item: GaoUser): string {
|
function buildUserAvatar(item: ManagedUser): string {
|
||||||
return isUser(item.user) ? UserAPI.getAvatarURL(item.user) || buildFallbackAvatar(getUserName(item)) : buildFallbackAvatar(getUserName(item));
|
return isUser(item.user) ? UserAPI.getAvatarURL(item.user) || buildFallbackAvatar(getUserName(item)) : buildFallbackAvatar(getUserName(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,22 +85,22 @@ function buildFallbackAvatar(name: string): string {
|
|||||||
|
|
||||||
async function loadData(): Promise<void> {
|
async function loadData(): Promise<void> {
|
||||||
const [userResult, actions] = await Promise.all([
|
const [userResult, actions] = await Promise.all([
|
||||||
GaoUserAPI.list({
|
ManagedUserAPI.list({
|
||||||
index: 0,
|
index: 0,
|
||||||
size: 100
|
size: 100
|
||||||
}),
|
}),
|
||||||
RegistrationEventAPI.enabledList(),
|
EventAPI.enabledList(),
|
||||||
permissionStore.loadAll()
|
permissionStore.loadAll()
|
||||||
]);
|
]);
|
||||||
userList.value = userResult.list || [];
|
userList.value = userResult.list || [];
|
||||||
registrationEventList.value = actions || [];
|
eventList.value = actions || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function goCreate(): Promise<void> {
|
async function goCreate(): Promise<void> {
|
||||||
await router.push("/management/users/create");
|
await router.push("/management/users/create");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function goDetail(item: GaoUser): Promise<void> {
|
async function goDetail(item: ManagedUser): Promise<void> {
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -193,3 +193,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<t-dropdown-item
|
<t-dropdown-item
|
||||||
:label="activeUserLabel"
|
:label="activeUserLabel"
|
||||||
:options="userOptions"
|
:options="userOptions"
|
||||||
:value="activeGaoUserId"
|
:value="activeManagedUserId"
|
||||||
@change="handleUserChange"
|
@change="handleUserChange"
|
||||||
/>
|
/>
|
||||||
</t-dropdown-menu>
|
</t-dropdown-menu>
|
||||||
@@ -43,21 +43,21 @@
|
|||||||
import type { DropdownOption, DropdownValue } from "tdesign-mobile-vue";
|
import type { DropdownOption, DropdownValue } from "tdesign-mobile-vue";
|
||||||
import { Toast } from "tdesign-mobile-vue";
|
import { Toast } from "tdesign-mobile-vue";
|
||||||
import { OrderType, RoleAPI, type ModuleCode, type Role } from "timi-web";
|
import { OrderType, RoleAPI, type ModuleCode, type Role } from "timi-web";
|
||||||
import GaoUserAPI from "@/api/GaoUserAPI";
|
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
import { corePermissionCodes } from "@/utils/gaoPermission";
|
import { corePermissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "UserRoleAssign"
|
name: "UserRoleAssign"
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const gaoModuleCode: ModuleCode = "GAO";
|
const moduleCode: ModuleCode = "GAO";
|
||||||
const pageSize = 1000;
|
const pageSize = 1000;
|
||||||
const userList = ref<GaoUser[]>([]);
|
const userList = ref<ManagedUser[]>([]);
|
||||||
const roleList = ref<Role[]>([]);
|
const roleList = ref<Role[]>([]);
|
||||||
const routeGaoUserId = computed(() => String(route.params.id || route.query.gaoUserId || ""));
|
const routeManagedUserId = computed(() => String(route.params.id || route.query.managedUserId || ""));
|
||||||
const activeGaoUserId = ref(routeGaoUserId.value);
|
const activeManagedUserId = ref(routeManagedUserId.value);
|
||||||
const checkedRoleIds = ref<string[]>([]);
|
const checkedRoleIds = ref<string[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
@@ -70,7 +70,7 @@ const userRoleWriteAccess = {
|
|||||||
],
|
],
|
||||||
mode: "and"
|
mode: "and"
|
||||||
} as const;
|
} as const;
|
||||||
const activeUser = computed(() => userList.value.find(item => item.id === activeGaoUserId.value));
|
const activeUser = computed(() => userList.value.find(item => item.id === activeManagedUserId.value));
|
||||||
const activeUserLabel = computed(() => activeUser.value ? `当前用户:${getUserName(activeUser.value)}` : "选择用户");
|
const activeUserLabel = computed(() => activeUser.value ? `当前用户:${getUserName(activeUser.value)}` : "选择用户");
|
||||||
const userOptions = computed<DropdownOption[]>(() => userList.value
|
const userOptions = computed<DropdownOption[]>(() => userList.value
|
||||||
.filter(item => !!item.id)
|
.filter(item => !!item.id)
|
||||||
@@ -81,7 +81,7 @@ const userOptions = computed<DropdownOption[]>(() => userList.value
|
|||||||
})));
|
})));
|
||||||
|
|
||||||
async function handleUserChange(value: DropdownValue): Promise<void> {
|
async function handleUserChange(value: DropdownValue): Promise<void> {
|
||||||
activeGaoUserId.value = Array.isArray(value) ? String(value[0] || "") : String(value);
|
activeManagedUserId.value = Array.isArray(value) ? String(value[0] || "") : String(value);
|
||||||
await loadActiveUserRoles();
|
await loadActiveUserRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,16 +89,16 @@ async function loadData(): Promise<void> {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const [userResult, roles] = await Promise.all([
|
const [userResult, roles] = await Promise.all([
|
||||||
GaoUserAPI.list({
|
ManagedUserAPI.list({
|
||||||
index: 0,
|
index: 0,
|
||||||
size: 100
|
size: 100
|
||||||
}),
|
}),
|
||||||
loadGaoRoleList()
|
loadRoleList()
|
||||||
]);
|
]);
|
||||||
userList.value = userResult.list || [];
|
userList.value = userResult.list || [];
|
||||||
roleList.value = roles || [];
|
roleList.value = roles || [];
|
||||||
if (!activeGaoUserId.value) {
|
if (!activeManagedUserId.value) {
|
||||||
activeGaoUserId.value = userList.value[0]?.id || "";
|
activeManagedUserId.value = userList.value[0]?.id || "";
|
||||||
}
|
}
|
||||||
await loadActiveUserRoles();
|
await loadActiveUserRoles();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -107,17 +107,17 @@ async function loadData(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadActiveUserRoles(): Promise<void> {
|
async function loadActiveUserRoles(): Promise<void> {
|
||||||
const gaoUserId = activeGaoUserId.value;
|
const managedUserId = activeManagedUserId.value;
|
||||||
const gaoUser = activeUser.value;
|
const managedUser = activeUser.value;
|
||||||
if (!gaoUserId || !gaoUser?.userId) {
|
if (!managedUserId || !managedUser?.userId) {
|
||||||
checkedRoleIds.value = [];
|
checkedRoleIds.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentSeq = roleSyncSeq + 1;
|
const currentSeq = roleSyncSeq + 1;
|
||||||
roleSyncSeq = currentSeq;
|
roleSyncSeq = currentSeq;
|
||||||
const roles = await RoleAPI.authorizedList(gaoUser.userId, gaoModuleCode);
|
const roles = await RoleAPI.authorizedList(managedUser.userId, moduleCode);
|
||||||
if (currentSeq !== roleSyncSeq || activeGaoUserId.value !== gaoUserId) {
|
if (currentSeq !== roleSyncSeq || activeManagedUserId.value !== managedUserId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ async function saveRoleList(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
await RoleAPI.authorize({
|
await RoleAPI.authorize({
|
||||||
userId: current.userId,
|
userId: current.userId,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
roleIdList: [...checkedRoleIds.value]
|
roleIdList: [...checkedRoleIds.value]
|
||||||
});
|
});
|
||||||
Toast({
|
Toast({
|
||||||
@@ -152,16 +152,16 @@ async function saveRoleList(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserName(item: GaoUser): string {
|
function getUserName(item: ManagedUser): string {
|
||||||
return item.user?.nick || item.user?.name || item.user?.phoneNo || "未命名用户";
|
return item.user?.nick || item.user?.name || item.user?.phoneNo || "未命名用户";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadGaoRoleList(): Promise<Role[]> {
|
async function loadRoleList(): Promise<Role[]> {
|
||||||
const result = await RoleAPI.list({
|
const result = await RoleAPI.list({
|
||||||
index: 0,
|
index: 0,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
equalsExample: {
|
equalsExample: {
|
||||||
moduleCode: gaoModuleCode
|
moduleCode: moduleCode
|
||||||
},
|
},
|
||||||
orderMap: {
|
orderMap: {
|
||||||
code: OrderType.ASC
|
code: OrderType.ASC
|
||||||
@@ -239,3 +239,4 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useUserStore } from "timi-web";
|
import { useUserStore } from "timi-web";
|
||||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CustomerTab"
|
name: "CustomerTab"
|
||||||
@@ -26,28 +26,28 @@ const userStore = useUserStore();
|
|||||||
const entryList = [
|
const entryList = [
|
||||||
{
|
{
|
||||||
path: "/customer/quick-entry",
|
path: "/customer/quick-entry",
|
||||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
permission: permissionCodes.eventRecord.create,
|
||||||
icon: "scan",
|
icon: "scan",
|
||||||
title: "快速补录登记",
|
title: "快速补录登记",
|
||||||
desc: "对以往未绑定二维码的客户进行快速补录和登记"
|
desc: "对以往未绑定二维码的客户进行快速补录和登记"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/customer/quick-sign-in",
|
path: "/customer/quick-sign-in",
|
||||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
permission: permissionCodes.eventRecord.create,
|
||||||
icon: "scan",
|
icon: "scan",
|
||||||
title: "快速扫码登记",
|
title: "快速扫码登记",
|
||||||
desc: "扫码选择客户并按配置事件登记"
|
desc: "扫码选择客户并按配置事件登记"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/customers/create?redirect=/customers",
|
path: "/management/customers/create?redirect=/customers",
|
||||||
permission: gaoPermissionCodes.customer.create,
|
permission: permissionCodes.customer.create,
|
||||||
icon: "edit-1",
|
icon: "edit-1",
|
||||||
title: "录入",
|
title: "录入",
|
||||||
desc: "新建客户档案并完善基础资料"
|
desc: "新建客户档案并完善基础资料"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/customer/registration",
|
path: "/customer/registration",
|
||||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
permission: permissionCodes.eventRecord.create,
|
||||||
icon: "task-checked",
|
icon: "task-checked",
|
||||||
title: "登记",
|
title: "登记",
|
||||||
desc: "按流程完成客户登记"
|
desc: "按流程完成客户登记"
|
||||||
@@ -127,3 +127,4 @@ async function go(path: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { UserAPI, useUserStore } from "timi-web";
|
import { UserAPI, useUserStore } from "timi-web";
|
||||||
import { corePermissionCodes, coreRoleCodes, gaoPermissionCodes } from "@/utils/gaoPermission";
|
import { corePermissionCodes, coreRoleCodes, permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
type UserAvatarSource = Parameters<typeof UserAPI.getAvatarURL>[0];
|
type UserAvatarSource = Parameters<typeof UserAPI.getAvatarURL>[0];
|
||||||
|
|
||||||
@@ -98,31 +98,31 @@ const menuGroupList = computed<MenuGroup[]>(() => [
|
|||||||
icon: "usergroup",
|
icon: "usergroup",
|
||||||
path: "/management/customers",
|
path: "/management/customers",
|
||||||
title: "客户管理",
|
title: "客户管理",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.customer.read)
|
visible: userStore.hasPermissions(permissionCodes.customer.read)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "task",
|
icon: "task",
|
||||||
path: "/management/registration-events",
|
path: "/management/registration-events",
|
||||||
title: "登记事件",
|
title: "登记事件",
|
||||||
visible: userStore.hasPermissions([gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read], "and")
|
visible: userStore.hasPermissions([permissionCodes.event.read, permissionCodes.user.read], "and")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "history",
|
icon: "history",
|
||||||
path: "/management/registration-event-records",
|
path: "/management/registration-event-records",
|
||||||
title: "登记事件记录",
|
title: "登记事件记录",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.registrationEventRecord.read)
|
visible: userStore.hasPermissions(permissionCodes.eventRecord.read)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "usergroup",
|
icon: "usergroup",
|
||||||
path: "/management/customer-data-charts",
|
path: "/management/customer-data-charts",
|
||||||
title: "客户统计数据",
|
title: "客户统计数据",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.stat.read)
|
visible: userStore.hasPermissions(permissionCodes.stat.read)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "chart",
|
icon: "chart",
|
||||||
path: "/management/data-charts",
|
path: "/management/data-charts",
|
||||||
title: "全局统计数据",
|
title: "全局统计数据",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.stat.read)
|
visible: userStore.hasPermissions(permissionCodes.stat.read)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -145,13 +145,13 @@ const menuGroupList = computed<MenuGroup[]>(() => [
|
|||||||
icon: "root-list",
|
icon: "root-list",
|
||||||
path: "/management/users",
|
path: "/management/users",
|
||||||
title: "用户管理",
|
title: "用户管理",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.user.read)
|
visible: userStore.hasPermissions(permissionCodes.user.read)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "qrcode",
|
icon: "qrcode",
|
||||||
path: "/management/qr-code",
|
path: "/management/qr-code",
|
||||||
title: "生成客户二维码",
|
title: "生成客户二维码",
|
||||||
visible: userStore.hasPermissions(gaoPermissionCodes.qrCode.create)
|
visible: userStore.hasPermissions(permissionCodes.qrCode.create)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -228,3 +228,4 @@ async function go(path: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { RouteRecordRaw } from "vue-router";
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
import QuickEntryPage from "@/pages/customer/QuickEntryPage.vue";
|
import QuickEntryPage from "@/pages/customer/QuickEntryPage.vue";
|
||||||
import QuickSignInPage from "@/pages/customer/QuickSignInPage.vue";
|
import QuickSignInPage from "@/pages/customer/QuickSignInPage.vue";
|
||||||
import RegistrationEventRecordEdit from "@/pages/management/RegistrationEventRecordEdit.vue";
|
import EventRecordEdit from "@/pages/management/EventRecordEdit.vue";
|
||||||
import CustomerList from "@/pages/management/CustomerList.vue";
|
import CustomerList from "@/pages/management/CustomerList.vue";
|
||||||
import CustomerDetail from "@/pages/management/CustomerDetail.vue";
|
import CustomerDetail from "@/pages/management/CustomerDetail.vue";
|
||||||
import CustomerEdit from "@/pages/management/CustomerEdit.vue";
|
import CustomerEdit from "@/pages/management/CustomerEdit.vue";
|
||||||
@@ -37,8 +37,8 @@ const customerRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/customer/registration",
|
path: "/customer/registration",
|
||||||
name: "CustomerRegistrationEventRecordCreate",
|
name: "CustomerEventRecordCreate",
|
||||||
component: RegistrationEventRecordEdit,
|
component: EventRecordEdit,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 3,
|
depth: 3,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -108,3 +108,4 @@ const customerRoutes: RouteRecordRaw[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default customerRoutes;
|
export default customerRoutes;
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { RouteRecordRaw } from "vue-router";
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
import RegistrationEventEdit from "@/pages/management/RegistrationEventEdit.vue";
|
import EventEdit from "@/pages/management/EventEdit.vue";
|
||||||
import RegistrationEventList from "@/pages/management/RegistrationEventList.vue";
|
import eventList from "@/pages/management/EventList.vue";
|
||||||
import RegistrationEventSort from "@/pages/management/RegistrationEventSort.vue";
|
import EventSort from "@/pages/management/EventSort.vue";
|
||||||
import RegistrationEventRecordDetail from "@/pages/management/RegistrationEventRecordDetail.vue";
|
import EventRecordDetail from "@/pages/management/EventRecordDetail.vue";
|
||||||
import RegistrationEventRecordEdit from "@/pages/management/RegistrationEventRecordEdit.vue";
|
import EventRecordEdit from "@/pages/management/EventRecordEdit.vue";
|
||||||
import RegistrationEventRecordList from "@/pages/management/RegistrationEventRecordList.vue";
|
import EventRecordList from "@/pages/management/EventRecordList.vue";
|
||||||
|
|
||||||
const registrationRoutes: RouteRecordRaw[] = [
|
const registrationRoutes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: "/management/registration-events",
|
path: "/management/registration-events",
|
||||||
name: "RegistrationEventList",
|
name: "eventList",
|
||||||
component: RegistrationEventList,
|
component: eventList,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 3,
|
depth: 3,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -23,8 +23,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-events/sort",
|
path: "/management/registration-events/sort",
|
||||||
name: "RegistrationEventSort",
|
name: "EventSort",
|
||||||
component: RegistrationEventSort,
|
component: EventSort,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 4,
|
depth: 4,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -37,8 +37,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-events/create",
|
path: "/management/registration-events/create",
|
||||||
name: "RegistrationEventCreate",
|
name: "EventCreate",
|
||||||
component: RegistrationEventEdit,
|
component: EventEdit,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 4,
|
depth: 4,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -51,8 +51,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-events/:id/edit",
|
path: "/management/registration-events/:id/edit",
|
||||||
name: "RegistrationEventEdit",
|
name: "EventEdit",
|
||||||
component: RegistrationEventEdit,
|
component: EventEdit,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 4,
|
depth: 4,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -65,8 +65,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-event-records",
|
path: "/management/registration-event-records",
|
||||||
name: "RegistrationEventRecordList",
|
name: "EventRecordList",
|
||||||
component: RegistrationEventRecordList,
|
component: EventRecordList,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 3,
|
depth: 3,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -79,8 +79,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-event-records/create",
|
path: "/management/registration-event-records/create",
|
||||||
name: "RegistrationEventRecordCreate",
|
name: "EventRecordCreate",
|
||||||
component: RegistrationEventRecordEdit,
|
component: EventRecordEdit,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 4,
|
depth: 4,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -93,8 +93,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-event-records/:id",
|
path: "/management/registration-event-records/:id",
|
||||||
name: "RegistrationEventRecordDetail",
|
name: "EventRecordDetail",
|
||||||
component: RegistrationEventRecordDetail,
|
component: EventRecordDetail,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 4,
|
depth: 4,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -107,8 +107,8 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/management/registration-event-records/:id/edit",
|
path: "/management/registration-event-records/:id/edit",
|
||||||
name: "RegistrationEventRecordEdit",
|
name: "EventRecordEdit",
|
||||||
component: RegistrationEventRecordEdit,
|
component: EventRecordEdit,
|
||||||
meta: {
|
meta: {
|
||||||
depth: 5,
|
depth: 5,
|
||||||
navBarVisible: true,
|
navBarVisible: true,
|
||||||
@@ -122,3 +122,4 @@ const registrationRoutes: RouteRecordRaw[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default registrationRoutes;
|
export default registrationRoutes;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
type RolePayload
|
type RolePayload
|
||||||
} from "timi-web";
|
} from "timi-web";
|
||||||
|
|
||||||
export const gaoModuleCode: ModuleCode = "GAO";
|
export const moduleCode: ModuleCode = "GAO";
|
||||||
const pageSize = 1000;
|
const pageSize = 1000;
|
||||||
|
|
||||||
export type Permission = UserPermission;
|
export type Permission = UserPermission;
|
||||||
@@ -41,7 +41,7 @@ export type PermissionDraft = {
|
|||||||
function normalizeRole(role: UserRole): Role {
|
function normalizeRole(role: UserRole): Role {
|
||||||
return {
|
return {
|
||||||
...role,
|
...role,
|
||||||
moduleCode: role.moduleCode || gaoModuleCode,
|
moduleCode: role.moduleCode || moduleCode,
|
||||||
description: role.description || "",
|
description: role.description || "",
|
||||||
permissionIdList: role.permissionIdList || role.permissionList?.map(item => item.id) || [],
|
permissionIdList: role.permissionIdList || role.permissionList?.map(item => item.id) || [],
|
||||||
childRoleIdList: role.childRoleIdList || role.childRoleList?.map(item => item.id) || []
|
childRoleIdList: role.childRoleIdList || role.childRoleList?.map(item => item.id) || []
|
||||||
@@ -51,7 +51,7 @@ function normalizeRole(role: UserRole): Role {
|
|||||||
function createEmptyRole(): Role {
|
function createEmptyRole(): Role {
|
||||||
return {
|
return {
|
||||||
id: "",
|
id: "",
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: "",
|
code: "",
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -63,7 +63,7 @@ function createEmptyRole(): Role {
|
|||||||
function createEmptyPermission(): Permission {
|
function createEmptyPermission(): Permission {
|
||||||
return {
|
return {
|
||||||
id: "",
|
id: "",
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: "",
|
code: "",
|
||||||
name: "",
|
name: "",
|
||||||
description: ""
|
description: ""
|
||||||
@@ -102,7 +102,7 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
index: 0,
|
index: 0,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
equalsExample: {
|
equalsExample: {
|
||||||
moduleCode: gaoModuleCode
|
moduleCode: moduleCode
|
||||||
},
|
},
|
||||||
orderMap: {
|
orderMap: {
|
||||||
code: OrderType.ASC
|
code: OrderType.ASC
|
||||||
@@ -112,7 +112,7 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
index: 0,
|
index: 0,
|
||||||
size: pageSize,
|
size: pageSize,
|
||||||
equalsExample: {
|
equalsExample: {
|
||||||
moduleCode: gaoModuleCode
|
moduleCode: moduleCode
|
||||||
},
|
},
|
||||||
orderMap: {
|
orderMap: {
|
||||||
code: OrderType.ASC
|
code: OrderType.ASC
|
||||||
@@ -228,7 +228,7 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
const current = getRoleById(draft.id || "");
|
const current = getRoleById(draft.id || "");
|
||||||
const payload: RolePayload = {
|
const payload: RolePayload = {
|
||||||
...draft,
|
...draft,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: draft.code.trim(),
|
code: draft.code.trim(),
|
||||||
nameLangId: current?.nameLangId,
|
nameLangId: current?.nameLangId,
|
||||||
name: draft.name.trim(),
|
name: draft.name.trim(),
|
||||||
@@ -270,7 +270,7 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
const current = getPermissionById(draft.id || "");
|
const current = getPermissionById(draft.id || "");
|
||||||
const payload: PermissionPayload = {
|
const payload: PermissionPayload = {
|
||||||
...draft,
|
...draft,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: draft.code.trim(),
|
code: draft.code.trim(),
|
||||||
nameLangId: current?.nameLangId,
|
nameLangId: current?.nameLangId,
|
||||||
name: draft.name.trim(),
|
name: draft.name.trim(),
|
||||||
@@ -307,7 +307,7 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
const role = await loadRoleDetail(roleId);
|
const role = await loadRoleDetail(roleId);
|
||||||
await upsertRole({
|
await upsertRole({
|
||||||
id: role.id,
|
id: role.id,
|
||||||
moduleCode: gaoModuleCode,
|
moduleCode: moduleCode,
|
||||||
code: role.code,
|
code: role.code,
|
||||||
name: role.name || "",
|
name: role.name || "",
|
||||||
description: role.description || "",
|
description: role.description || "",
|
||||||
@@ -337,3 +337,4 @@ export const usePermissionSettingsStore = defineStore("permission-settings", ()
|
|||||||
setRolePermissionList
|
setRolePermissionList
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export type CustomerInviteUpdateReq = {
|
|||||||
|
|
||||||
export type QuickRegisterReq = {
|
export type QuickRegisterReq = {
|
||||||
customerCode: string;
|
customerCode: string;
|
||||||
registrationEventId: string;
|
eventId: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
attachmentIdList?: string[];
|
attachmentIdList?: string[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Model } from "timi-web";
|
||||||
|
|
||||||
|
export type EventStatus = "DRAFT" | "ACTIVE" | "INACTIVE" | "DELETED";
|
||||||
|
|
||||||
|
export type EventLimitType = "NONE" | "DAILY_HALF_DAY" | "NATURAL_DAY" | "INTERVAL_HOUR" | "NATURAL_MONTH" | "TOTAL_LIMIT" | "CUSTOMER_TOTAL_LIMIT";
|
||||||
|
|
||||||
|
export type EventItem = {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
color?: string;
|
||||||
|
status?: EventStatus;
|
||||||
|
requirePhoto?: boolean;
|
||||||
|
limitType?: EventLimitType;
|
||||||
|
limitValue?: number;
|
||||||
|
beginAt?: number;
|
||||||
|
endAt?: number;
|
||||||
|
sort?: number;
|
||||||
|
roleCodeList?: string[];
|
||||||
|
} & Model;
|
||||||
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import type { Attachment, Model, User } from "timi-web";
|
||||||
|
import type { Customer } from "@/types/Customer";
|
||||||
|
import type { EventItem } from "@/types/EventItem";
|
||||||
|
|
||||||
|
export type EventRecord = {
|
||||||
|
customerId?: string;
|
||||||
|
eventId?: string;
|
||||||
|
operatorUserId?: string;
|
||||||
|
remark?: string;
|
||||||
|
registeredAt?: number;
|
||||||
|
customer?: Customer;
|
||||||
|
event?: EventItem;
|
||||||
|
operator?: Partial<User>;
|
||||||
|
attachmentList?: Array<Partial<Attachment>>;
|
||||||
|
attachmentIdList?: string[];
|
||||||
|
} & Model;
|
||||||
|
|
||||||
|
export type EventRecordListItemView = {
|
||||||
|
id?: string;
|
||||||
|
customerId?: string;
|
||||||
|
customerName?: string;
|
||||||
|
customerPhotoAttachmentId?: string;
|
||||||
|
eventId?: string;
|
||||||
|
eventName?: string;
|
||||||
|
registeredAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordPage = {
|
||||||
|
customerId?: string;
|
||||||
|
eventId?: string;
|
||||||
|
eventIdList?: string[];
|
||||||
|
keyword?: string;
|
||||||
|
beginAt: number;
|
||||||
|
endAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordPageReq = {
|
||||||
|
index: number;
|
||||||
|
size: number;
|
||||||
|
customerId?: string;
|
||||||
|
eventIdList?: string[];
|
||||||
|
keyword?: string;
|
||||||
|
beginAt?: number;
|
||||||
|
endAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordStatReq = {
|
||||||
|
customerId?: string;
|
||||||
|
beginAt?: number;
|
||||||
|
endAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordTrendStatReq = {
|
||||||
|
customerId?: string;
|
||||||
|
eventId: string;
|
||||||
|
beginAt: number;
|
||||||
|
endAt: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CustomerEventStatView = {
|
||||||
|
customerId?: string;
|
||||||
|
customerName?: string;
|
||||||
|
eventId?: string;
|
||||||
|
eventName?: string;
|
||||||
|
count?: number;
|
||||||
|
latestRegisteredAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CustomerEventRankView = {
|
||||||
|
rank?: number;
|
||||||
|
customerId?: string;
|
||||||
|
customerCode?: string;
|
||||||
|
customerName?: string;
|
||||||
|
customerPhotoAttachmentId?: string;
|
||||||
|
totalEventDays?: number;
|
||||||
|
totalEventCount?: number;
|
||||||
|
recentContinuousEventDays?: number;
|
||||||
|
latestRegisteredAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CustomerEventRankResponse = {
|
||||||
|
rank?: number;
|
||||||
|
customerId?: string;
|
||||||
|
customer?: Customer;
|
||||||
|
totalEventDays?: number;
|
||||||
|
totalEventCount?: number;
|
||||||
|
recentContinuousEventDays?: number;
|
||||||
|
latestRegisteredAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordDailyStatView = {
|
||||||
|
dateValue?: number;
|
||||||
|
dailyCount?: number;
|
||||||
|
totalCount?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordCalendarView = {
|
||||||
|
id?: string;
|
||||||
|
customerId?: string;
|
||||||
|
customerCode?: string;
|
||||||
|
customerName?: string;
|
||||||
|
eventId?: string;
|
||||||
|
registeredAt?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EventRecordCreateReq = {
|
||||||
|
customerIdList: string[];
|
||||||
|
eventId: string;
|
||||||
|
remark?: string;
|
||||||
|
registeredAt?: number;
|
||||||
|
attachmentIdList?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Model, Role, User } from "timi-web";
|
import type { Model, Role, User } from "timi-web";
|
||||||
|
|
||||||
export type GaoUser = {
|
export type ManagedUser = {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
@@ -8,3 +8,4 @@ export type GaoUser = {
|
|||||||
roleList?: Array<Role | string>;
|
roleList?: Array<Role | string>;
|
||||||
roleIdList?: string[];
|
roleIdList?: string[];
|
||||||
} & Model;
|
} & Model;
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import type { Model } from "timi-web";
|
|
||||||
|
|
||||||
export type RegistrationEventStatus = "DRAFT" | "ACTIVE" | "INACTIVE" | "DELETED";
|
|
||||||
|
|
||||||
export type RegistrationLimitType = "NONE" | "DAILY_HALF_DAY" | "NATURAL_DAY" | "INTERVAL_HOUR" | "NATURAL_MONTH" | "TOTAL_LIMIT" | "CUSTOMER_TOTAL_LIMIT";
|
|
||||||
|
|
||||||
export type RegistrationEvent = {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
color?: string;
|
|
||||||
status?: RegistrationEventStatus;
|
|
||||||
requirePhoto?: boolean;
|
|
||||||
limitType?: RegistrationLimitType;
|
|
||||||
limitValue?: number;
|
|
||||||
beginAt?: number;
|
|
||||||
endAt?: number;
|
|
||||||
sort?: number;
|
|
||||||
roleCodeList?: string[];
|
|
||||||
} & Model;
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import type { Attachment, Model, User } from "timi-web";
|
|
||||||
import type { Customer } from "@/types/Customer";
|
|
||||||
import type { RegistrationEvent } from "@/types/RegistrationEvent";
|
|
||||||
|
|
||||||
export type RegistrationEventRecord = {
|
|
||||||
customerId?: string;
|
|
||||||
registrationEventId?: string;
|
|
||||||
operatorUserId?: string;
|
|
||||||
remark?: string;
|
|
||||||
registeredAt?: number;
|
|
||||||
customer?: Customer;
|
|
||||||
registrationEvent?: RegistrationEvent;
|
|
||||||
operator?: Partial<User>;
|
|
||||||
attachmentList?: Array<Partial<Attachment>>;
|
|
||||||
attachmentIdList?: string[];
|
|
||||||
} & Model;
|
|
||||||
|
|
||||||
export type RegistrationEventRecordListItemView = {
|
|
||||||
id?: string;
|
|
||||||
customerId?: string;
|
|
||||||
customerName?: string;
|
|
||||||
customerPhotoAttachmentId?: string;
|
|
||||||
registrationEventId?: string;
|
|
||||||
registrationEventName?: string;
|
|
||||||
registeredAt?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordRangeReq = {
|
|
||||||
customerId?: string;
|
|
||||||
registrationEventId?: string;
|
|
||||||
registrationEventIdList?: string[];
|
|
||||||
keyword?: string;
|
|
||||||
beginRegisteredAt: number;
|
|
||||||
endRegisteredAt: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordStatReq = {
|
|
||||||
customerId?: string;
|
|
||||||
beginRegisteredAt?: number;
|
|
||||||
endRegisteredAt?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordTrendStatReq = {
|
|
||||||
customerId?: string;
|
|
||||||
registrationEventId: string;
|
|
||||||
beginRegisteredAt: number;
|
|
||||||
endRegisteredAt: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CustomerRegisterStatView = {
|
|
||||||
customerId?: string;
|
|
||||||
customerName?: string;
|
|
||||||
registrationEventId?: string;
|
|
||||||
registrationEventName?: string;
|
|
||||||
count?: number;
|
|
||||||
latestRegisteredAt?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CustomerRegistrationRankView = {
|
|
||||||
rank?: number;
|
|
||||||
customerId?: string;
|
|
||||||
customerCode?: string;
|
|
||||||
customerName?: string;
|
|
||||||
customerPhotoAttachmentId?: string;
|
|
||||||
totalRegistrationDays?: number;
|
|
||||||
totalRegistrationCount?: number;
|
|
||||||
recentContinuousRegistrationDays?: number;
|
|
||||||
latestRegisteredAt?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordDailyStatView = {
|
|
||||||
dateValue?: number;
|
|
||||||
dailyCount?: number;
|
|
||||||
totalCount?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordCalendarView = {
|
|
||||||
id?: string;
|
|
||||||
customerId?: string;
|
|
||||||
customerCode?: string;
|
|
||||||
customerName?: string;
|
|
||||||
registrationEventId?: string;
|
|
||||||
registeredAt?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegistrationEventRecordCreateReq = {
|
|
||||||
customerIdList: string[];
|
|
||||||
registrationEventId: string;
|
|
||||||
remark?: string;
|
|
||||||
registeredAt?: number;
|
|
||||||
attachmentIdList?: string[];
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AttachmentAPI } from "timi-web";
|
import { AttachmentAPI } from "timi-web";
|
||||||
import type { RegistrationEventRecord } from "@/types/RegistrationEventRecord";
|
import type { EventRecord } from "@/types/EventRecord";
|
||||||
|
|
||||||
export function getRegistrationEventRecordPhotoURL(record?: RegistrationEventRecord): string {
|
export function getEventRecordPhotoURL(record?: EventRecord): string {
|
||||||
const attachment = record?.attachmentList?.find(item => item.attachType === "PHOTO") || record?.attachmentList?.[0];
|
const attachment = record?.attachmentList?.find(item => item.attachType === "PHOTO") || record?.attachmentList?.[0];
|
||||||
if (!attachment?.id) {
|
if (!attachment?.id) {
|
||||||
return "";
|
return "";
|
||||||
@@ -9,3 +9,4 @@ export function getRegistrationEventRecordPhotoURL(record?: RegistrationEventRec
|
|||||||
|
|
||||||
return AttachmentAPI.getReadURL(attachment.id);
|
return AttachmentAPI.getReadURL(attachment.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,16 +3,31 @@ import imageCompression from "browser-image-compression";
|
|||||||
const PHOTO_MAX_SIZE_MB = 300 / 1024;
|
const PHOTO_MAX_SIZE_MB = 300 / 1024;
|
||||||
const PHOTO_MAX_WIDTH_OR_HEIGHT = 1920;
|
const PHOTO_MAX_WIDTH_OR_HEIGHT = 1920;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为真正的 File,避免压缩库返回 Blob 后 FormData 使用默认文件名 blob。
|
||||||
|
*/
|
||||||
|
function normalizeUploadImageFile(source: File, compressed: Blob): File {
|
||||||
|
if (compressed instanceof File && compressed.name) {
|
||||||
|
return compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new File([compressed], source.name || "upload-image", {
|
||||||
|
type: compressed.type || source.type,
|
||||||
|
lastModified: source.lastModified
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 压缩上传图片,统一限制到 300 KB。
|
* 压缩上传图片,统一限制到 300 KB。
|
||||||
*/
|
*/
|
||||||
export async function compressUploadImage(file: File): Promise<File> {
|
export async function compressUploadImage(file: File): Promise<File> {
|
||||||
return await imageCompression(file, {
|
const compressedFile = await imageCompression(file, {
|
||||||
maxSizeMB: PHOTO_MAX_SIZE_MB,
|
maxSizeMB: PHOTO_MAX_SIZE_MB,
|
||||||
maxWidthOrHeight: PHOTO_MAX_WIDTH_OR_HEIGHT,
|
maxWidthOrHeight: PHOTO_MAX_WIDTH_OR_HEIGHT,
|
||||||
useWebWorker: true,
|
useWebWorker: true,
|
||||||
initialQuality: .86
|
initialQuality: .86
|
||||||
});
|
});
|
||||||
|
return normalizeUploadImageFile(file, compressedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const gaoPermissionCodes = {
|
export const permissionCodes = {
|
||||||
customer: {
|
customer: {
|
||||||
create: "GAO:CUSTOMER:CREATE",
|
create: "GAO:CUSTOMER:CREATE",
|
||||||
read: "GAO:CUSTOMER:READ",
|
read: "GAO:CUSTOMER:READ",
|
||||||
@@ -6,18 +6,18 @@ export const gaoPermissionCodes = {
|
|||||||
update: "GAO:CUSTOMER:UPDATE",
|
update: "GAO:CUSTOMER:UPDATE",
|
||||||
delete: "GAO:CUSTOMER:DELETE"
|
delete: "GAO:CUSTOMER:DELETE"
|
||||||
},
|
},
|
||||||
registrationEvent: {
|
event: {
|
||||||
create: "GAO:REGISTRATION_EVENT:CREATE",
|
create: "GAO:EVENT:CREATE",
|
||||||
read: "GAO:REGISTRATION_EVENT:READ",
|
read: "GAO:EVENT:READ",
|
||||||
update: "GAO:REGISTRATION_EVENT:UPDATE",
|
update: "GAO:EVENT:UPDATE",
|
||||||
delete: "GAO:REGISTRATION_EVENT:DELETE"
|
delete: "GAO:EVENT:DELETE"
|
||||||
},
|
},
|
||||||
registrationEventRecord: {
|
eventRecord: {
|
||||||
create: "GAO:REGISTRATION_EVENT_RECORD:CREATE",
|
create: "GAO:EVENT_RECORD:CREATE",
|
||||||
read: "GAO:REGISTRATION_EVENT_RECORD:READ",
|
read: "GAO:EVENT_RECORD:READ",
|
||||||
export: "GAO:REGISTRATION_EVENT_RECORD:EXPORT",
|
export: "GAO:EVENT_RECORD:EXPORT",
|
||||||
update: "GAO:REGISTRATION_EVENT_RECORD:UPDATE",
|
update: "GAO:EVENT_RECORD:UPDATE",
|
||||||
delete: "GAO:REGISTRATION_EVENT_RECORD:DELETE"
|
delete: "GAO:EVENT_RECORD:DELETE"
|
||||||
},
|
},
|
||||||
stat: {
|
stat: {
|
||||||
read: "GAO:STAT:READ"
|
read: "GAO:STAT:READ"
|
||||||
@@ -76,5 +76,6 @@ export const permissionManagementRoleAccess = {
|
|||||||
mode: "or"
|
mode: "or"
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type GaoPermissionCodes = typeof gaoPermissionCodes;
|
export type PermissionCodes = typeof permissionCodes;
|
||||||
export type CorePermissionCodes = typeof corePermissionCodes;
|
export type CorePermissionCodes = typeof corePermissionCodes;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Role } from "@/store/permissionSettingsStore";
|
import type { Role } from "@/store/permissionSettingsStore";
|
||||||
import type { GaoUser } from "@/types/GaoUser";
|
import type { ManagedUser } from "@/types/ManagedUser";
|
||||||
|
|
||||||
type GaoUserRole = NonNullable<GaoUser["roleList"]>[number];
|
type ManagedUserRole = NonNullable<ManagedUser["roleList"]>[number];
|
||||||
|
|
||||||
function buildRoleCode(role: Role): string {
|
function buildRoleCode(role: Role): string {
|
||||||
return role.moduleCode && !role.code.includes(":") ? `${role.moduleCode}:${role.code}` : role.code;
|
return role.moduleCode && !role.code.includes(":") ? `${role.moduleCode}:${role.code}` : role.code;
|
||||||
@@ -12,7 +12,7 @@ function getBareRoleCode(code: string): string {
|
|||||||
return codePartList[codePartList.length - 1] || code;
|
return codePartList[codePartList.length - 1] || code;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findRole(role: GaoUserRole, roleList: Role[]): Role | undefined {
|
function findRole(role: ManagedUserRole, roleList: Role[]): Role | undefined {
|
||||||
if (typeof role === "string") {
|
if (typeof role === "string") {
|
||||||
return roleList.find(roleItem => roleItem.code === role || buildRoleCode(roleItem) === role);
|
return roleList.find(roleItem => roleItem.code === role || buildRoleCode(roleItem) === role);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ function appendCompatibleRoleCode(roleCodeSet: Set<string>, code: string): void
|
|||||||
roleCodeSet.add(getBareRoleCode(code));
|
roleCodeSet.add(getBareRoleCode(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGaoRoleName(role: GaoUserRole | undefined, roleList: Role[]): string {
|
export function getRoleDisplayName(role: ManagedUserRole | undefined, roleList: Role[]): string {
|
||||||
if (!role) {
|
if (!role) {
|
||||||
return "未配置角色";
|
return "未配置角色";
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ export function getGaoRoleName(role: GaoUserRole | undefined, roleList: Role[]):
|
|||||||
return typeof role === "string" ? getBareRoleCode(role) : role.name || role.code || "未配置角色";
|
return typeof role === "string" ? getBareRoleCode(role) : role.name || role.code || "未配置角色";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildEffectiveRoleCodeSet(user: GaoUser, roleList: Role[]): Set<string> {
|
export function buildEffectiveRoleCodeSet(user: ManagedUser, roleList: Role[]): Set<string> {
|
||||||
const roleCodeSet = new Set<string>();
|
const roleCodeSet = new Set<string>();
|
||||||
for (const role of user.roleList || []) {
|
for (const role of user.roleList || []) {
|
||||||
const fullRole = findRole(role, roleList);
|
const fullRole = findRole(role, roleList);
|
||||||
@@ -68,3 +68,4 @@ function appendRoleCode(roleCodeSet: Set<string>, role: Role, roleList: Role[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user