feat: 搭建微信小程序展示端
- 初始化小程序工程配置与类型声明 - 增加首页、律所、律师列表、详情与历史页面 - 补充公共组件、运行时配置与示例素材
This commit is contained in:
65
frontend_miniprogram/miniprogram/utils/history.ts
Normal file
65
frontend_miniprogram/miniprogram/utils/history.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { CARD_VIEW_HISTORY_KEY } from '../constants/storage';
|
||||
import type { CardViewRecord, Lawyer } from '../types/card';
|
||||
|
||||
function normalizeLawyerSnapshot(input: unknown): Lawyer | undefined {
|
||||
if (!input || typeof input !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const lawyer = input as Partial<Lawyer>;
|
||||
if (typeof lawyer.id !== 'string' || !lawyer.id) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
id: lawyer.id,
|
||||
name: typeof lawyer.name === 'string' ? lawyer.name : '',
|
||||
title: typeof lawyer.title === 'string' ? lawyer.title : '',
|
||||
office: typeof lawyer.office === 'string' ? lawyer.office : '',
|
||||
phone: typeof lawyer.phone === 'string' ? lawyer.phone : '',
|
||||
email: typeof lawyer.email === 'string' ? lawyer.email : '',
|
||||
address: typeof lawyer.address === 'string' ? lawyer.address : '',
|
||||
avatar: typeof lawyer.avatar === 'string' ? lawyer.avatar : '',
|
||||
coverImage: typeof lawyer.coverImage === 'string' ? lawyer.coverImage : '',
|
||||
specialties: Array.isArray(lawyer.specialties)
|
||||
? lawyer.specialties.filter((item): item is string => typeof item === 'string')
|
||||
: [],
|
||||
bio: typeof lawyer.bio === 'string' ? lawyer.bio : '',
|
||||
wechatQrImage: typeof lawyer.wechatQrImage === 'string' ? lawyer.wechatQrImage : '',
|
||||
};
|
||||
}
|
||||
|
||||
export function getCardViewHistory(): CardViewRecord[] {
|
||||
const raw = wx.getStorageSync(CARD_VIEW_HISTORY_KEY);
|
||||
if (!Array.isArray(raw)) {
|
||||
return [];
|
||||
}
|
||||
return raw
|
||||
.map((item) => {
|
||||
const record = item as { lawyerId?: unknown; viewedAt?: unknown; lawyer?: unknown };
|
||||
const lawyerId = typeof record.lawyerId === 'string' ? record.lawyerId : '';
|
||||
const viewedAt = typeof record.viewedAt === 'number' ? record.viewedAt : 0;
|
||||
const lawyer = normalizeLawyerSnapshot(record.lawyer);
|
||||
return { lawyerId, viewedAt, lawyer };
|
||||
})
|
||||
.filter((item) => Boolean(item.lawyerId) && item.viewedAt > 0)
|
||||
.sort((a, b) => b.viewedAt - a.viewedAt);
|
||||
}
|
||||
|
||||
export function appendCardViewRecord(lawyer: Lawyer): void {
|
||||
if (!lawyer.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current = getCardViewHistory().filter((item) => item.lawyerId !== lawyer.id);
|
||||
current.unshift({
|
||||
lawyerId: lawyer.id,
|
||||
viewedAt: Date.now(),
|
||||
lawyer,
|
||||
});
|
||||
wx.setStorageSync(CARD_VIEW_HISTORY_KEY, current);
|
||||
}
|
||||
|
||||
export function clearCardViewHistory(): void {
|
||||
wx.removeStorageSync(CARD_VIEW_HISTORY_KEY);
|
||||
}
|
||||
75
frontend_miniprogram/miniprogram/utils/http.ts
Normal file
75
frontend_miniprogram/miniprogram/utils/http.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { API_BASE_URL_OVERRIDE_KEY, tenantRuntimeConfig } from '../config/runtime';
|
||||
import { getMiniappAppId, getMiniappEnvVersion } from './miniapp';
|
||||
|
||||
interface ApiResponse<T> {
|
||||
code: string;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
interface RequestOptions {
|
||||
url: string;
|
||||
method?: 'GET' | 'POST';
|
||||
data?: WechatMiniprogram.IAnyObject;
|
||||
}
|
||||
|
||||
function normalizeBaseUrl(baseUrl: string): string {
|
||||
return baseUrl.trim().replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
function getApiBaseUrl(): string {
|
||||
const override = wx.getStorageSync(API_BASE_URL_OVERRIDE_KEY);
|
||||
if (typeof override === 'string' && override.trim()) {
|
||||
return normalizeBaseUrl(override);
|
||||
}
|
||||
|
||||
const envVersion = getMiniappEnvVersion();
|
||||
const baseUrl = tenantRuntimeConfig.apiBaseUrlByEnv[envVersion];
|
||||
if (!baseUrl || !baseUrl.trim()) {
|
||||
throw new Error(`未配置 ${envVersion} 环境的接口域名`);
|
||||
}
|
||||
|
||||
const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
|
||||
if (envVersion !== 'develop' && !normalizedBaseUrl.startsWith('https://')) {
|
||||
throw new Error(`${envVersion} 环境接口域名必须使用 HTTPS`);
|
||||
}
|
||||
|
||||
return normalizedBaseUrl;
|
||||
}
|
||||
|
||||
export function request<T>(options: RequestOptions): Promise<T> {
|
||||
const appId = getMiniappAppId();
|
||||
if (!appId) {
|
||||
return Promise.reject(new Error('未获取到小程序 AppID'));
|
||||
}
|
||||
|
||||
let apiBaseUrl = '';
|
||||
try {
|
||||
apiBaseUrl = getApiBaseUrl();
|
||||
} catch (error) {
|
||||
return Promise.reject(error instanceof Error ? error : new Error('接口域名配置无效'));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: `${apiBaseUrl}${options.url}`,
|
||||
method: options.method || 'GET',
|
||||
data: options.data,
|
||||
header: {
|
||||
'X-Miniapp-Appid': appId,
|
||||
},
|
||||
success: (response) => {
|
||||
const payload = response.data as ApiResponse<T> | undefined;
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && payload && payload.code === '0') {
|
||||
resolve(payload.data);
|
||||
return;
|
||||
}
|
||||
const message = payload && payload.message ? payload.message : '请求失败';
|
||||
reject(new Error(message));
|
||||
},
|
||||
fail: () => {
|
||||
reject(new Error('网络异常,请稍后重试'));
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
29
frontend_miniprogram/miniprogram/utils/miniapp.ts
Normal file
29
frontend_miniprogram/miniprogram/utils/miniapp.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { MiniProgramEnvVersion } from '../config/runtime';
|
||||
|
||||
const APPID_OVERRIDE_KEY = 'miniapp_appid_override';
|
||||
|
||||
export function getMiniappAppId(): string {
|
||||
const override = wx.getStorageSync(APPID_OVERRIDE_KEY);
|
||||
if (typeof override === 'string' && override.trim()) {
|
||||
return override.trim();
|
||||
}
|
||||
|
||||
try {
|
||||
const accountInfo = wx.getAccountInfoSync();
|
||||
return accountInfo.miniProgram.appId || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function getMiniappEnvVersion(): MiniProgramEnvVersion {
|
||||
try {
|
||||
const envVersion = wx.getAccountInfoSync().miniProgram.envVersion;
|
||||
if (envVersion === 'trial' || envVersion === 'release') {
|
||||
return envVersion;
|
||||
}
|
||||
return 'develop';
|
||||
} catch {
|
||||
return 'develop';
|
||||
}
|
||||
}
|
||||
19
frontend_miniprogram/miniprogram/utils/util.ts
Normal file
19
frontend_miniprogram/miniprogram/utils/util.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const formatTime = (date: Date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const hour = date.getHours()
|
||||
const minute = date.getMinutes()
|
||||
const second = date.getSeconds()
|
||||
|
||||
return (
|
||||
[year, month, day].map(formatNumber).join('/') +
|
||||
' ' +
|
||||
[hour, minute, second].map(formatNumber).join(':')
|
||||
)
|
||||
}
|
||||
|
||||
const formatNumber = (n: number) => {
|
||||
const s = n.toString()
|
||||
return s[1] ? s : '0' + s
|
||||
}
|
||||
Reference in New Issue
Block a user