| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- import { DropdownDirective } from '../../shared/dropdown.directive';
- import { CommonModule } from '@angular/common';
- import { ChangeDetectionStrategy, Component, inject, OnInit, signal } from '@angular/core';
- import { FormsModule } from '@angular/forms';
- import { ActivatedRoute, Router } from '@angular/router';
- import { AlertCircle, ChevronRight, Inbox, LucideAngularModule, Plus, RefreshCw, Send, Trash2, X } from 'lucide-angular';
- import { finalize } from 'rxjs';
- import { ApiError, ApiService } from '../../core/api.service';
- import { actionMatches, buildActionPayload, buildItemNavigation, buildLoadParams, itemId } from '../../core/business-rules';
- import { PageAction, PageMeta } from '../../core/models';
- import { PlatformService } from '../../core/platform.service';
- import { SessionService } from '../../core/session.service';
- type DataItem = Record<string, unknown>;
- @Component({
- selector: 'app-resource-page',
- standalone: true,
- imports: [DropdownDirective, CommonModule, FormsModule, LucideAngularModule],
- templateUrl: './resource-page.component.html',
- styleUrl: './resource-page.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush,
- })
- export class ResourcePageComponent implements OnInit {
- readonly meta = inject(ActivatedRoute).snapshot.data['meta'] as PageMeta;
- readonly items = signal<DataItem[]>([]);
- readonly detail = signal<DataItem | null>(null);
- readonly error = signal('');
- readonly loading = signal(false);
- readonly activeTab = signal(0);
- readonly selectedAction = signal<PageAction | null>(null);
- readonly selectedItem = signal<DataItem | null>(null);
- readonly contextOrder = signal<DataItem | null>(null);
- readonly contextCarts = signal<DataItem[]>([]);
- readonly icons = { AlertCircle, ChevronRight, Inbox, Plus, RefreshCw, Send, Trash2, X };
- actionValues: Record<string, unknown> = {};
- private readonly route = inject(ActivatedRoute);
- private readonly router = inject(Router);
- private readonly api = inject(ApiService);
- private readonly session = inject(SessionService);
- private readonly platform = inject(PlatformService);
- ngOnInit(): void {
- if (this.meta.path === 'pages/shop/order_comment') this.loadOrderContext();
- else this.load();
- }
- load(): void {
- if (!this.meta.api) return;
- this.loading.set(true);
- this.error.set('');
- const user = this.session.user();
- const action = this.meta.tabActions?.[this.activeTab()] ?? this.meta.api.action;
- const binding = { ...this.meta.api, action };
- const params = buildLoadParams(this.meta, binding, this.route.snapshot.queryParams, user, this.activeTab());
- const request = binding.method === 'post'
- ? this.api.post<unknown>(action, params)
- : this.api.get<unknown>(action, params);
- request.pipe(finalize(() => this.loading.set(false))).subscribe({
- next: (response) => {
- const value = this.pickPath(response.result, this.meta.api?.resultPath);
- const list = this.attachAddon(this.asList(value), response.addon);
- this.items.set(this.meta.path === 'pages/member/ranklist' ? list.map((item, index) => ({ ...item, index: index + 1 })) : list);
- this.detail.set(list.length ? null : this.asObject(value));
- },
- error: (error: ApiError) => {
- if (this.meta.path === 'pages/member/ranklist' && error.message.includes('接口不存在')) {
- this.items.set([]);
- this.error.set('');
- return;
- }
- this.error.set(error.message);
- },
- });
- for (const binding of this.meta.onLoadActions ?? []) {
- const extraPayload = buildLoadParams(this.meta, binding, this.route.snapshot.queryParams, user, this.activeTab());
- if (binding.action === 'node_get' && !extraPayload['id']) extraPayload['id'] = extraPayload['nid'] ?? extraPayload['pnid'];
- const extra = binding.method === 'post' ? this.api.post(binding.action, extraPayload) : this.api.get(binding.action, extraPayload);
- extra.subscribe({ error: () => undefined });
- }
- }
- selectTab(index: number): void {
- this.activeTab.set(index);
- this.load();
- }
- open(item: DataItem): void {
- if (this.meta.path === 'pages/member/ranklist' && this.activeTab() === 0) return;
- const target = buildItemNavigation(this.meta, item);
- if (target) void this.router.navigate([target.path], { queryParams: target.queryParams });
- }
- addRoute(): string | null {
- return ({
- 'pages/member/purse_outacc': '/pages/member/purse_outacc_add',
- 'pages/member/receaddr': '/pages/member/receaddr_add',
- 'pages/member/invoice': '/pages/member/invoice_add',
- 'pages/member/myshop': '/pages/member/myshop_add',
- 'pages/shop/order_comment': '/pages/shop/order_comment_add',
- } as Record<string, string>)[this.meta.path] ?? null;
- }
- create(): void {
- const target = this.addRoute();
- const queryParams = this.meta.path === 'pages/shop/order_comment' ? { id: this.route.snapshot.queryParamMap.get('id') } : {};
- if (target) void this.router.navigate([target], { queryParams });
- }
- async remove(item: DataItem, event: Event): Promise<void> {
- event.stopPropagation();
- if (!this.meta.deleteAction || !(await this.platform.confirm('确认删除这条记录?'))) return;
- const action: PageAction = { label: '删除', action: this.meta.deleteAction, scope: 'item', success: 'remove-item' };
- const payload = buildActionPayload(this.meta, action, this.route.snapshot.queryParams, this.session.user(), {}, item);
- this.platform.loading.set(true);
- this.api.post(this.meta.deleteAction, payload).pipe(finalize(() => this.platform.loading.set(false))).subscribe({
- next: () => {
- this.platform.toast('删除成功', 'success');
- this.items.update((items) => items.filter((entry) => entry !== item));
- },
- error: (error: ApiError) => this.platform.toast(error.message, 'error'),
- });
- }
- beginAction(action: PageAction, item: DataItem | null = null, event?: Event): void {
- event?.stopPropagation();
- this.actionValues = {};
- this.selectedItem.set(item);
- this.selectedAction.set(action);
- if (!action.fields?.length) void this.submitAction(action);
- }
- closeAction(): void {
- this.selectedAction.set(null);
- this.selectedItem.set(null);
- this.actionValues = {};
- }
- async submitAction(action = this.selectedAction()): Promise<void> {
- if (!action) return;
- if (action.confirm && !(await this.platform.confirm(action.confirm))) return;
- for (const field of action.fields ?? []) {
- if (field.required && !String(this.actionValues[field.key] ?? '').trim()) {
- this.platform.toast(`请填写${field.label}`, 'error');
- return;
- }
- }
- const item = this.selectedItem();
- const payload = buildActionPayload(this.meta, action, this.route.snapshot.queryParams, this.session.user(), this.actionValues, item);
- this.platform.loading.set(true);
- const request = action.method === 'get' ? this.api.get(action.action, payload) : this.api.post(action.action, payload);
- request.pipe(finalize(() => this.platform.loading.set(false))).subscribe({
- next: () => {
- this.platform.toast(`${action.label}成功`, 'success');
- if (action.success === 'remove-item' && item) this.items.update((items) => items.filter((entry) => entry !== item));
- this.closeAction();
- if (action.success !== 'none' && action.success !== 'remove-item') this.load();
- },
- error: (error: ApiError) => this.platform.toast(error.message, 'error'),
- });
- }
- titleOf(item: DataItem): string {
- return String(item['Title'] ?? item['Proname'] ?? item['OrderNo'] ?? item['ArriveName'] ?? item['InvHead'] ?? item['ReceivName'] ?? item['PeopleName'] ?? item['title'] ?? item['name'] ?? item['HoneyName'] ?? item['UserName'] ?? `记录 ${this.itemId(item) || ''}`);
- }
- bodyOf(item: DataItem): string { return String(item['MsgContent'] ?? item['PubContent'] ?? ''); }
- scoreOf(item: DataItem): number { return Math.max(0, Math.min(5, Number(item['Score'] ?? 0))); }
- contextProduct(item: DataItem): DataItem { return this.asObject(item['_product']) ?? {}; }
- contextProductName(item: DataItem): string { return String(item['Proname'] ?? this.parseObject(item['ProInfo'])['Proname'] ?? '商品'); }
- contextProductImage(item: DataItem): string { return this.api.assetUrl(item['proimg'] ?? this.parseObject(item['ProInfo'])['Thumbnails']); }
- subtitleOf(item: DataItem): string {
- if (this.meta.path === 'pages/member/ranklist') {
- return this.activeTab() === 0
- ? `团队规模:${item['num'] ?? 0} · 第${item['index'] ?? '-'}名`
- : `${this.activeTab() === 4 ? '回购数量' : '销量'}:${item['num'] ?? 0} · 第${item['index'] ?? '-'}名`;
- }
- return String(item['Abstract'] ?? item['synopsis'] ?? item['Remind'] ?? item['AddTime'] ?? item['CDate'] ?? item['Status'] ?? item['NodeName'] ?? '').replace(/<[^>]*>/g, '').slice(0, 120);
- }
- imageOf(item: DataItem): string | null {
- const carts = Array.isArray(item['cart']) ? item['cart'] as DataItem[] : [];
- const productInfo = this.parseObject(carts[0]?.['ProInfo']);
- const linkedProduct = this.contextProduct(item);
- const raw = item['TopImg'] ?? item['Thumbnails'] ?? item['Pic'] ?? item['UserFace'] ?? item['userFace'] ?? item['Image'] ?? linkedProduct['proimg'] ?? this.parseObject(linkedProduct['ProInfo'])['Thumbnails'] ?? carts[0]?.['proimg'] ?? productInfo['Thumbnails'];
- return raw ? this.api.assetUrl(raw) : null;
- }
- itemId(item: DataItem): string | number | null { return itemId(item); }
- itemActions(item: DataItem): PageAction[] {
- return (this.meta.actions ?? []).filter((action) => {
- if (action.scope !== 'item' || !actionMatches(action, item)) return false;
- if (this.meta.path === 'pages/member/myshop' && action.key === 'stop-sale') return this.activeTab() === 0;
- if (this.meta.path === 'pages/member/myshop' && action.key === 'start-sale') return this.activeTab() === 2;
- return true;
- });
- }
- detailEntries(): Array<{ key: string; value: string }> {
- const detail = this.detail();
- if (!detail) return [];
- return Object.entries(detail)
- .filter(([, value]) => value !== null && value !== undefined && typeof value !== 'object')
- .slice(0, 24)
- .map(([key, value]) => ({ key, value: String(value) }));
- }
- private asList(value: unknown): DataItem[] {
- if (Array.isArray(value)) return value.filter(this.isObject);
- if (!this.isObject(value)) return [];
- for (const key of ['list', 'items', 'data', 'CartDT', 'OrderList', 'Result', 'rows']) {
- const nested = value[key];
- if (Array.isArray(nested)) return nested.filter(this.isObject);
- }
- for (const nested of Object.values(value)) if (Array.isArray(nested)) return nested.filter(this.isObject);
- return [];
- }
- private asObject(value: unknown): DataItem | null { return this.isObject(value) ? value : null; }
- private isObject(value: unknown): value is DataItem { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); }
- private pickPath(value: unknown, path?: string): unknown {
- if (!path) return value;
- return path.split('.').reduce<unknown>((current, key) => this.isObject(current) ? current[key] : undefined, value);
- }
- private attachAddon(list: DataItem[], addon: unknown): DataItem[] {
- if (this.meta.path === 'pages/member/myshop_order' && Array.isArray(addon)) {
- return list.map((order) => ({
- ...order,
- cart: addon.filter((entry) => this.isObject(entry) && String(entry['ID']) === String(order['ID'])),
- }));
- }
- if (this.meta.path === 'pages/shop/order_comment') {
- return list.map((comment) => ({ ...comment, _product: this.contextCarts().find((cart) => String(cart['ProID']) === String(comment['ProID'])) }));
- }
- return list;
- }
- private loadOrderContext(): void {
- const id = this.route.snapshot.queryParamMap.get('id');
- if (!id) { this.error.set('缺少订单编号'); return; }
- this.loading.set(true);
- this.api.get<unknown>('order_get', { id, uid: this.session.user()?.userId }).subscribe({
- next: ({ result, addon }) => {
- this.contextOrder.set(this.asObject(result));
- const extra = this.asObject(addon) ?? {};
- const carts = Array.isArray(extra['cart']) ? extra['cart'] as DataItem[] : [];
- this.contextCarts.set(carts.map((item) => {
- const product = this.parseObject(item['ProInfo']);
- return { ...product, ...item, proimg: item['proimg'] ?? product['Thumbnails'] };
- }));
- this.load();
- },
- error: (error: ApiError) => { this.loading.set(false); this.error.set(`订单加载失败:${error.message}`); },
- });
- }
- private parseObject(value: unknown): DataItem {
- if (this.isObject(value)) return value;
- try { return JSON.parse(String(value ?? '{}')) as DataItem; } catch { return {}; }
- }
- }
|