소스 검색

feat: first commit

chenyixian 3 달 전
커밋
5df611b7b1

+ 9 - 0
config/config.ts

@@ -0,0 +1,9 @@
+/** 接口请求地址 */
+// 需要代理的时候,将这个设置为 api/
+export const BASE_URL = 'https://cs001.kasitesoft.com/';
+/** 接口请求地址(代理用) */
+export const PROXY_URL = 'https://cs001.kasitesoft.com/';
+/** 接口是否加密 */
+export const IS_ENCRYPT = false;
+/** 接口超时时间 默认1分钟 */
+export const TIMEOUT = 60000;

+ 551 - 0
config/frontEndConfig.ts

@@ -0,0 +1,551 @@
+export const frontEndConfig = {
+	// 固定的GlobalData配置信息,只存在本地代码',
+	fixedAppjsGlobalData: {
+		// 防止页面并发调用登录接口,默认判断5次',
+		retryNum: 5,
+		// 院区Id 如果院区ID有值 queryBHospitalList和挂号流程 会使用此ID, 默认取DIY配置 院区列表中第一个院区ID',
+		districtId: '',
+		// 医院Id   demo 1024727 43',
+		hosId: '1024727',
+		// 小程序登录configKey',
+		configKey: 'WXTEST001',
+		// *配置appId,后端分配',
+		// appId: 'wxede0b125eed31b0d',
+		// 公众号授权地址configKey',
+		wechatConfigKey: 'kasite_demo',
+		// 请求接口需要加密请求的秘钥,要与配置的相同',
+		apiSecretKey: 'sdf67UI23f123',
+		// 不需要加密的请求接口',
+		noNeedSecretKeyApiArr: [],
+	},
+	// 可配置修改的GlobalData配置信息,与下方的webDiy配置一起配置后台配置系统内',
+	appjsGlobalData: {
+		// 存储医院信息经纬度,在开发是先找项目经理要,用于来院导航、与在线签到',
+		hospitalInfo: {
+			Wd: '26.06063',
+			Jd: '119.26848',
+			HospitalAddress: '福建省福州市仓山区',
+			HospitalAlias: '福州智医科技(演示医院)',
+		},
+		// 判断当前服务器是否更新,主要用于上线项目,后端全量更新时使用',
+		updateOrNot: false,
+		// 是否区分院区',
+		hasDistrict: false,
+		// 是否开启公众号授权',
+		officialAuthOn: true,
+		// 是否允许无卡预约',
+		withoutCard: true,
+		// 是否需要互联网Websocket',
+		hasWebsocket: true,
+		// 是否需要适老版',
+		hasSlb: true,
+		// 上门护理配置信息',
+		nurseConfig: {},
+		// 上门护理默认HosId',
+		nurseHosId: '1024727',
+		// 是否开启AI智能客服',
+		showAICustomerService: true,
+	},
+	webUiDiy: {
+		pageConfiguration: {
+			currency_config: {
+				remark: '通用配置',
+				remarkMsg: 'authBusinessPage:要授权的业务页面;',
+				authBusinessPage: {
+					remarkMsg:
+						'pageName:授权业务页面名称;levelNum:授权页面需求用户等级1:短信验证;2人脸认证;[reportIndex:报告查询]',
+					pageName: 'levelNum',
+					reportIndex: '1',
+				},
+			},
+			login_config: {
+				messageSys: true,
+			},
+			homePage_config: {
+				remark: '就诊人列表 页面个性化',
+				remarkMsg: 'medicalBtn:展示医保电子',
+				medicalBtn: false,
+			},
+			memberList_config: {
+				remark: '就诊人列表 页面个性化',
+				remarkMsg: 'idCodeShow:是否展示电子就诊码;',
+				isCodeShow: true,
+			},
+			hospitalDistrict_config: {
+				remark: '院区选择 页面个性化配置',
+				remarkMsg:
+					'districtList:院区列表;districImg:院区图片;districtName: 院区名称;districtId:院区ID',
+				districtList: [
+					{
+						districImg:
+							'https://demo.kasitesoft.com/uploadFile/ui/image/menu/homePage_menuYygk.png',
+						districtName: '智医科技体验医院',
+						districtId: '1024727',
+					},
+					{
+						districImg:
+							'https://demo.kasitesoft.com/uploadFile/ui/image/menu/homePage_menuLcfb.png',
+						districtName: '智医科技体验医院分院',
+						districtId: '10247271',
+					},
+				],
+			},
+			yyghDeptList_config: {
+				remark: '科室列表 页面个性化配置',
+				remarkMsg:
+					'aiMode:ai到诊配置模块;showDeptSec:是否展示二级科室;deptShowMode:查看具体科室排班时弹窗提示;modalData:弹窗内容具体配置信息查看[pages.st1.components.richTextModal.richTextModal]',
+				aiMode: {
+					showAi: true,
+					appId: 'wxba22f1a66a3af7aa',
+					path: 'pages/index?appid=wx3f6fa5f164160c4b',
+				},
+				showDeptSec: false,
+				deptShowMode: {
+					科室code: '特殊科室提示内容',
+				},
+				modalData: {
+					showModal: false,
+					title: '预约须知',
+					content: '',
+					contentAlign: '',
+					styleType: 'bottom',
+					articleId: '70a53d226c5e41149b6e9d7f9863cbd4',
+					showCancel: false,
+					cancelText: '取消',
+					cancelColor: 'red',
+					confirmText: '确定',
+					showCancelBtn: true,
+				},
+			},
+			yyghDoctorList_config: {
+				remark: '医生列表 页面个性化配置',
+				remarkMsg: 'hasAllSchedules:排班时间是否有全部选项;',
+				hasAllSchedules: true,
+			},
+			yyghClinicMsg_config: {
+				remark: '确认挂号 页面个性化配置',
+				remarkMsg: 'numberType:号源展示  1方块格式  2列表格式;',
+				numberType: '1',
+			},
+			payState_config: {
+				remark: '订单成功页面个性化配置',
+				remarkMsg:
+					'isAiBtn:预约挂号AI预问诊是否展示;tip:预约挂号注意事项;tips:用于有用预约配置信息时,把内容复制到tip上,signInQueue:签单成功是否展示候诊',
+				isAiBtn: true,
+				tips: [
+					[
+						'1、签到取号:',
+						'于就诊当日到院签到,支持微信签到、自助机、服务台签到取号,可提前90分钟或延迟30分钟(超过30分钟则预约无效,需要重新排号);',
+					],
+					['2、退号:', '如您无法按时就诊,请至少提前1天取消,以免因为爽约影响下次预约;'],
+					['3、首次就诊患者:', '需要带身份证原件办理开卡;'],
+					[
+						'未实名制的复诊患者:',
+						'在就诊前需要持本人有效身份证证件进行实名认证,否则无法就诊;有效证件类型包括:身份证、社保卡、户口簿(仅限未成年)、护照、驾驶证、港澳台通行证、老人证、军官证、出生证(仅限新生儿)。',
+					],
+				],
+				signInQueue: true,
+			},
+			authorizeMode_config: {
+				remark: '授权组件 页面个性化配置',
+				remarkMsg: 'face:是否有人脸识别授权;code:是否有短信验证码授权;',
+				face: true,
+				code: true,
+			},
+			signInList_config: {
+				remark: '在线签到 页面个性化配置',
+				remarkMsg:
+					'allowedSignAfter:允许超时多久还可签到;allowedSignBefore:就诊前多久可签到;signDistance:签到距离;signReason:签到接口入参1旧接口2星网锐捷',
+				allowedSignAfter: 30,
+				allowedSignBefore: 30,
+				signDistance: 1,
+				signReason: 2,
+			},
+			orderPayment_config: {
+				remark: '门诊结算 页面个性化配置',
+				remarkMsg:
+					'orderPaymentMode:订单缴费模式  1预交金  2线上支付;consolidationPayment:是否合并支付;',
+				orderPaymentMode: '2',
+				consolidationPayment: false,
+			},
+			payMent_config: {
+				remark: '支付倒计时 页面个性化配置',
+				remarkMsg: 'payWay:支付方式:1为单个支付方式;2为多个支付方式选择;',
+				payWay: '1',
+			},
+			inspectTestReportDetails_config: {
+				remark: '检查报告单详情 页面个性化配置',
+				remarkMsg: 'showCloudFilm:是否展示云胶片按钮;',
+				showCloudFilm: false,
+			},
+			topUpRecord_screening: {
+				remark: '充值记录筛选',
+				remarkMsg:
+					'title(标题名称),key(参数 字段名),single(选择类型,true:单选,false:多选,默认多选),options(标题名称对应的筛选条件)',
+				value: [
+					{
+						title: '订单状态',
+						key: 'payState',
+						single: true,
+						options: [
+							{
+								name: '全部',
+								check: true,
+								value: {
+									OverState: '',
+									PayState: '',
+									BizState: '',
+								},
+							},
+							{
+								name: '未支付',
+								check: false,
+								value: {
+									OverState: '0',
+									PayState: '0',
+									BizState: '0',
+								},
+							},
+							{
+								name: '已支付',
+								check: false,
+								value: {
+									OverState: '0',
+									PayState: '2',
+									BizState: '1',
+								},
+							},
+							{
+								name: '支付中',
+								check: false,
+								value: {
+									OverState: '0',
+									PayState: '1',
+									BizState: '0',
+								},
+							},
+							{
+								name: '已取消',
+								check: false,
+								value: {
+									OverState: '5',
+									PayState: '0',
+									BizState: '0',
+								},
+							},
+						],
+					},
+					{
+						title: '订单类型',
+						key: 'serviceId',
+						options: [
+							{
+								name: '全部',
+								check: true,
+								value: '006,007',
+							},
+							{
+								name: '门诊充值',
+								check: false,
+								value: '006',
+							},
+							{
+								name: '住院充值',
+								check: false,
+								value: '007',
+							},
+						],
+					},
+				],
+			},
+			appointmentRecord_screening: {
+				remark: '预约记录筛选',
+				remarkMsg:
+					'type(选择类型,0:单选,1:多选,2:输入);title(标题名称);value(标题名称对应的筛选条件),parameter(参数 字段名)',
+				value: [
+					{
+						type: 0,
+						title: '预约记录',
+						selected: [
+							{
+								key: 0,
+								name: '全部',
+								options: {
+									OverState: '',
+									PayState: '',
+									BizState: '',
+								},
+							},
+						],
+						value: [
+							{
+								key: 0,
+								name: '全部',
+								options: {
+									OverState: '',
+									PayState: '',
+									BizState: '',
+								},
+							},
+							{
+								key: 1,
+								name: '已挂号',
+								options: {
+									OverState: '0',
+									PayState: '0',
+									BizState: '1',
+								},
+							},
+							{
+								key: 2,
+								name: '已退号',
+								options: {
+									OverState: '0',
+									PayState: '0',
+									BizState: '2',
+								},
+							},
+						],
+					},
+				],
+			},
+		},
+		net_pageConfiguration_patient: {
+			currency_config: {
+				remark: '通用配置',
+				remarkMsg:
+					'useMenuOnLine:是否从后台获取menu菜单; officialAuthOn:是否开启公众号授权;isVisitMode:是否为就诊人模式;withoutCard:就诊人模式是否允许无卡预约;queryMemberList:是否使用queryMemberList查询就诊人;hospitalAddress:来源自取地址;',
+				iniHighAppraiseCount: 0,
+				consultIsOnlinePay: '1',
+				hospitalAddress: '福建省福州市仓山区',
+			},
+			reportIndex_config: {
+				remark: '查报告单 页面个性化配置',
+				remarkMsg: 'auth:是否需要授权;typeShow:报告查询两种模式: 1 tab切换模式; 2 列表选择',
+				auth: true,
+				typeShow: '2',
+			},
+			authorizeMode_config: {
+				remark: '授权组件 页面个性化配置',
+				remarkMsg: 'face:是否有人脸识别授权;code:是否有短信验证码授权;',
+				face: false,
+				code: true,
+			},
+			inspectTestReportDetails_config: {
+				remark: '检查报告单详情 页面个性化配置',
+				remarkMsg: 'showCloudFilm:是否展示云胶片按钮;',
+				showCloudFilm: true,
+			},
+			doctorList_screening: {
+				remark: '推荐医生筛选',
+				remarkMsg:
+					'type(选择类型,0:单选,1:多选,2:输入,3:日期选择);title(标题名称);value(标题名称对应的筛选条件)',
+				value: [
+					{
+						type: '0',
+						title: '开处方状态',
+						selectedInd: [0],
+						list: [
+							{
+								key: 0,
+								name: '全部状态',
+								options: {
+									DoctorService1004: '',
+								},
+							},
+							{
+								key: 0,
+								name: '可开处方',
+								options: {
+									DoctorService1004: '1',
+								},
+							},
+							{
+								key: 0,
+								name: '不可开处方',
+								options: {
+									DoctorService1004: '0',
+								},
+							},
+						],
+					},
+					{
+						type: '0',
+						title: '服务模式',
+						selectedInd: [0],
+						list: [
+							{
+								key: 0,
+								name: '全部方式',
+								options: {
+									ConsultType: '',
+								},
+							},
+							{
+								key: 0,
+								name: '图文咨询',
+								options: {
+									ConsultType: '1',
+								},
+							},
+							{
+								key: 0,
+								name: '视频咨询',
+								options: {
+									ConsultType: '2',
+								},
+							},
+						],
+					},
+				],
+			},
+			enquireList_screening: {
+				remark: '咨询中筛选',
+				remarkMsg:
+					'type(选择类型,0:单选,1:多选,2:输入,3:日期选择);title(标题名称);value(标题名称对应的筛选条件)',
+				value: [
+					{
+						type: '1',
+						title: '咨询状态',
+						selectedInd: [0],
+						list: [
+							{
+								key: 0,
+								name: '全部状态',
+								options: {
+									ConsultStatus: '',
+								},
+							},
+							{
+								key: 0,
+								name: '待支付',
+								options: {
+									ConsultStatus: '0',
+								},
+							},
+							{
+								key: 0,
+								name: '待接单',
+								options: {
+									ConsultStatus: '1',
+								},
+							},
+							{
+								key: 0,
+								name: '进行中',
+								options: {
+									ConsultStatus: '2',
+								},
+							},
+							{
+								key: 0,
+								name: '已结束',
+								options: {
+									ConsultStatus: '3',
+								},
+							},
+							{
+								key: 0,
+								name: '已评价',
+								options: {
+									ConsultStatus: '5',
+								},
+							},
+						],
+					},
+				],
+			},
+			logisticsRecord_screening: {
+				remark: '物流记录筛选',
+				remarkMsg:
+					'type(选择类型,0:单选,1:多选,2:输入,3:日期选择);title(标题名称);value(标题名称对应的筛选条件)',
+				value: [
+					{
+						type: '1',
+						title: '物流状态',
+						selectedInd: [0],
+						list: [
+							{
+								key: 0,
+								name: '全部状态',
+								options: {
+									ConsultStatus: '',
+								},
+							},
+							{
+								key: 0,
+								name: '待自取',
+								options: {
+									ConsultStatus: '1',
+								},
+							},
+							{
+								key: 0,
+								name: '已取药(已签收)',
+								options: {
+									ConsultStatus: '2',
+								},
+							},
+							{
+								key: 0,
+								name: '配送中',
+								options: {
+									ConsultStatus: '0',
+								},
+							},
+						],
+					},
+				],
+			},
+			rechargeRecord_screening: {
+				remark: '充值记录筛选',
+				remarkMsg:
+					'type(选择类型,0:单选,1:多选,2:输入);title(标题名称);value(标题名称对应的筛选条件),options(参数 字段名)',
+				value: [],
+			},
+			report_screening: {
+				remark: '报告单查询筛选',
+				state:
+					'type(选择类型,0:单选,1:多选,2:输入);title(标题名称);value(标题名称对应的筛选条件),options(参数 字段名)',
+				value: [
+					{
+						type: 0,
+						title: '报告单类型',
+						selected: [
+							{
+								key: 0,
+								name: '全部',
+								options: {
+									ReportType: '',
+								},
+							},
+						],
+						value: [
+							{
+								key: 0,
+								name: '全部',
+								options: {
+									ReportType: '',
+								},
+							},
+							{
+								key: 0,
+								name: '检验报告单',
+								options: {
+									ReportType: '1',
+								},
+							},
+							{
+								key: 1,
+								name: '检查报告单',
+								options: {
+									ReportType: '2',
+								},
+							},
+						],
+					},
+				],
+			},
+		},
+	},
+};

+ 2 - 0
config/index.ts

@@ -0,0 +1,2 @@
+export * as REQUEST_CONFIG from './config';
+export * from './frontEndConfig';

+ 12 - 0
hook/index.ts

@@ -0,0 +1,12 @@
+export * from './use-front-end-config-version';
+export * from './use-front-end-config';
+export * from './use-app-status';
+export * from './use-get-update-manager';
+export * from './use-small-program-login';
+export * from './use-set-front-end-form';
+export * from './use-set-menu-list';
+
+export * from './use-get-sys-app-page-list';
+export * from './use-preser-member';
+
+export * from './use-domain';

+ 23 - 0
hook/use-app-status/index.ts

@@ -0,0 +1,23 @@
+import { GetAppStatus } from '../../service/base';
+
+/** 判断app状态进行跳转 */
+export const useAppStatus = async () => {
+	const app = getApp();
+	const { status, content } = await GetAppStatus({
+		appId: app.globalData.appId,
+	});
+	switch (status) {
+		case 'maintain':
+			uni.reLaunch({
+				url: `/pages/st1/business/errorPage/maintain/maintain?content=${content}`,
+			});
+			break;
+		case 'offline':
+			uni.reLaunch({
+				url: '/pages/st1/business/errorPage/offline/offline',
+			});
+			break;
+		default:
+			break;
+	}
+};

+ 5 - 0
hook/use-domain/index.ts

@@ -0,0 +1,5 @@
+export const useDomain = () => {
+	const { origin, pathname } = location;
+	const hasSuffix = pathname.match(/\/KasiteWeb\//);
+	return `${origin}${hasSuffix ? '/KasiteWeb' : ''}`;
+};

+ 28 - 0
hook/use-front-end-config-version/index.ts

@@ -0,0 +1,28 @@
+import { REQUEST_CONFIG } from '../../config';
+
+/** 获取小程序配置信息版本号,用于控制是否加载前端缓存更新 */
+export const useFrontEndConfigVersion = async () => {
+	return new Promise((resolve) => {
+		uni.request({
+			url: `${REQUEST_CONFIG.BASE_URL}api/smallproFrontEndConfigVersion/localConfig.do`,
+			method: 'GET',
+			data: {},
+			success(resp: any) {
+				// 判断最新获取到的版本号是否与本地保持一致 保持一致修改缓存frontEndConfigRequest 用于判断当前是否要重新请求
+				if (uni.getStorageSync('frontEndConfigVersion') == resp.data.frontEndConfigVersion) {
+					uni.setStorageSync('frontEndConfigRequest', false);
+				} else {
+					// 不一致 代表当前前端配置信息更新
+					uni.setStorageSync('frontEndConfigRequest', true);
+					uni.setStorageSync('frontEndConfigVersion', resp.data.frontEndConfigVersion);
+				}
+				resolve(false);
+			},
+			fail(error) {
+				// 如果是因为网络错误一律,前端配置信息一律都重新请求
+				uni.setStorageSync('frontEndConfigRequest', true);
+				resolve(false);
+			},
+		});
+	});
+};

+ 92 - 0
hook/use-front-end-config/index.ts

@@ -0,0 +1,92 @@
+import { REQUEST_CONFIG, frontEndConfig } from '../../config';
+import { common } from '../../utils';
+
+// 获取前端统一缓存配置信息 并返回前端配置信息
+const frontEndConfigFn = () => {
+	return new Promise(async (resolve) => {
+		let accountInfo = getApp().globalData.accountInfo;
+		// 判断正式版本获取线上配置数据
+		if (accountInfo.miniProgram.envVersion == 'release') {
+			// 获取小程序初始数据
+			uni.request({
+				url: `${REQUEST_CONFIG.BASE_URL}api/wechaSmallproFrontEndConfig/localConfig.do`,
+				method: 'GET',
+				data: {},
+				header: {
+					'content-type': 'application/x-www-form-urlencoded',
+				},
+				success(resp: any) {
+					// 小程登录初始化数据信息 从本地获取
+					resp.data.fixedAppjsGlobalData = frontEndConfig.fixedAppjsGlobalData;
+					uni.setStorageSync('frontEndConfig', resp.data);
+					resolve(resp.data);
+				},
+				fail(error) {
+					resolve(false);
+				},
+			});
+		} else {
+			// 否则都取本地数据
+			uni.setStorageSync('frontEndConfig', frontEndConfig);
+			resolve(frontEndConfig);
+		}
+	});
+};
+
+/** 设置前端配置信息处理 */
+export const useSetFrontEndConfig = async () => {
+	let has = false;
+	let frontEndConfigs: any;
+	if (
+		uni.getStorageSync('frontEndConfigRequest') ||
+		common.isEmpty(uni.getStorageSync('frontEndConfig'))
+	) {
+		frontEndConfigs = await frontEndConfigFn();
+	} else {
+		frontEndConfigs = uni.getStorageSync('frontEndConfig');
+	}
+	return new Promise(async (resolve) => {
+		// 判断是否配置了小程序登录初始化数据信息
+		if (!frontEndConfigs.appjsGlobalData) {
+			common.showModal('小程序登录初始化数据信息未配置');
+			has = true;
+		} else {
+			/**版本信息 */
+			getApp().globalData = Object.assign(getApp().globalData, {
+				smallPro_version: getApp().globalData.accountInfo.miniProgram.version || '0.0.0', // 登录请求时版本信息, 本地和体验版本都是0.0.0,正式线用获取的
+				...frontEndConfigs.fixedAppjsGlobalData,
+				...frontEndConfigs.appjsGlobalData,
+			});
+		}
+		// 判断是否配置了前端webUiDiy配置信息
+		if (!frontEndConfigs.webUiDiy) {
+			common.showModal(
+				frontEndConfigs === false ? '网络请求异常,请切换网络重新进入!' : 'webUiDiy配置信息未配置'
+			);
+			uni.hideLoading();
+			has = true;
+		} else {
+			let config = frontEndConfigs.webUiDiy;
+			getApp().globalData.config = frontEndConfigs.webUiDiy;
+			/**如果配置了院区并且有区分院区  将院区ID设置为第一个院区的ID */
+			let distric =
+				config && config.pageConfiguration && config.pageConfiguration.hospitalDistrict_config;
+			if (
+				getApp().globalData.hasDistrict &&
+				distric &&
+				distric.districtList instanceof Array &&
+				distric.districtList[0].districtId
+			) {
+				getApp().globalData.districtId = distric.districtList[0].districtId;
+			}
+		}
+		// 判断当前服务器在更新 跳转温馨提示页
+		if (frontEndConfigs.appjsGlobalData.updateOrNot) {
+			uni.redirectTo({
+				url: `pages/st1/business/otherService/building/building`,
+			});
+			has = true;
+		}
+		resolve(has);
+	});
+};

+ 13 - 0
hook/use-get-sys-app-page-list/index.ts

@@ -0,0 +1,13 @@
+import { SysAppPageList } from '../../service/base';;
+import { common } from '../../utils';
+
+export const useGetSysAppPageList = async () => {
+	const app = getApp();
+	const resp = await SysAppPageList({
+		sysAppCode: app.globalData.appId,
+	});
+	if (resp) {
+		const map = common.turnToMap(resp);
+		app.globalData.pageMessageList = map;
+	}
+};

+ 36 - 0
hook/use-get-update-manager/index.ts

@@ -0,0 +1,36 @@
+/** 检查是否存在新版本 */
+export const useGetUpdateManager = () => {
+	return new Promise((resolve, reject) => {
+		uni.getUpdateManager().onCheckForUpdate(function (res) {
+			// 请求完新版本信息的回调
+			if (res.hasUpdate) {
+				//如果有新版本
+				// 小程序有新版本,会主动触发下载操作(无需开发者触发)
+				uni.getUpdateManager().onUpdateReady(function () {
+					//当新版本下载完成,会进行回调
+					uni.showModal({
+						title: '更新提示',
+						content: '新版本已经准备好,单击确定重启应用',
+						showCancel: false,
+						success: function (res) {
+							if (res.confirm) {
+								// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+								uni.getUpdateManager().applyUpdate();
+							}
+						},
+					});
+				});
+				// 小程序有新版本,会主动触发下载操作(无需开发者触发)
+				uni.getUpdateManager().onUpdateFailed(function () {
+					//当新版本下载失败,会进行回调
+					uni.showModal({
+						title: '提示',
+						content: '检查到有新版本,但下载失败,请检查网络设置',
+						showCancel: false,
+					});
+				});
+			}
+			resolve(res.hasUpdate);
+		});
+	});
+};

+ 22 - 0
hook/use-preser-member/index.ts

@@ -0,0 +1,22 @@
+import { QueryBaseMemberList_V3 } from '../../service/base';
+import { common } from '../../utils';
+
+/** 查询并保存全局所有就诊人数据信息 (就诊人改变后都要重新调用,并保存一遍)*/
+export const usePreserMember = async () => {
+	let queryData = {
+		isCache: false,
+		isEncrypt: true,
+		hosId: getApp().globalData.districtId || getApp().globalData.hosId,
+		openid: uni.getStorageSync('openid'),
+	};
+	let { resp, resData } = await QueryBaseMemberList_V3(queryData);
+	// 请求成功且有返回值保存全局就诊人数据列表
+	getApp().globalData.currentUser = null;
+	if (resData.RespCode == 10000 && common.isNotEmpty(resp)) {
+		uni.setStorageSync('memberList', resp);
+	} else {
+		// 否则保存空
+		uni.setStorageSync('memberList', null);
+	}
+	return resp;
+};

+ 21 - 0
hook/use-set-front-end-form/index.ts

@@ -0,0 +1,21 @@
+import { common } from '../../utils';
+import { BaseFormQuery } from '../../service/base';
+
+/** 设置form表单 */
+export const useSetFrontEndForm = () => {
+	return new Promise(async (resolve) => {
+		/**获取form表单配置 */
+		let queryData = {
+			api: 'base.form.Query',
+			formScene: '',
+			orgCode: getApp().globalData.hosId,
+		};
+		let formConfigList = await BaseFormQuery(queryData);
+		formConfigList = common.isEmpty(formConfigList) ? [] : formConfigList;
+		if (common.isEmpty(formConfigList)) {
+			common.showModal(`form表单未配置`);
+		}
+		uni.setStorageSync('formConfigList', formConfigList);
+		resolve(true);
+	});
+};

+ 25 - 0
hook/use-set-menu-list/index.ts

@@ -0,0 +1,25 @@
+import { common } from '../../utils';
+import { SysclientappmenuList } from '../../service/base';
+
+/** 设置菜单 */
+export const useSetMenuList = async (menu: any[]) => {
+	return new Promise(async (resolve) => {
+		// 获取菜单路由配置
+		let menuList = menu;
+		const app = getApp();
+		console.log(menuList);
+		/**判断当前是正式发布版本从线上获取 */
+		if (app.globalData.accountInfo.miniProgram.envVersion == 'release') {
+			let getSysConfigData = {
+				orgCode: app.globalData.hosId,
+				appId: app.globalData.appId,
+			};
+			let menuresp = await SysclientappmenuList(getSysConfigData);
+			if (!common.isEmpty(menuresp)) {
+				menuList = JSON.parse(menuresp[0].menuJson);
+			}
+		}
+		uni.setStorageSync('menuList', menuList);
+		resolve(menuList);
+	});
+};

+ 202 - 0
hook/use-small-program-login/index.ts

@@ -0,0 +1,202 @@
+import { REQUEST_CONFIG } from '../../config';
+import { WebOauth2Authorize } from '../../service/base';
+import { useDomain } from '../use-domain';
+
+/** 缓存是否过期 */
+export const useIsExpiration = async () => {
+	// 当前时间
+	var timestamp = Date.parse(new Date());
+	// 缓存中的过期时间
+	var data_expiration = uni.getStorageSync('data_expiration');
+	// 如果缓存中没有data_expiration,说明也没有token,还未登录
+	if (data_expiration) {
+		// 如果超时了,清除缓存,重新登录
+		if (timestamp > data_expiration) {
+			uni.setStorageSync('data_expiration', 0);
+			return false;
+		} else {
+			return true;
+		}
+	}
+	return false;
+};
+
+/** 小程序登录 */
+export const useSmallProgramLogin = async (app: App.AppInstance) => {
+	let res;
+	// #ifdef MP-WEIXIN
+	res = await smallProgramLoginByMPWEIXIN(app);
+	// #endif
+
+	// #ifdef MP-GONGZHONGHAO
+	res = await smallProgramLoginByGONGZHONGHAO(app);
+	// #endif
+
+	return res;
+};
+
+const smallProgramLoginByMPWEIXIN = async (app: App.AppInstance) => {
+	return new Promise(async (resolve, reject) => {
+		// 判断当前token是否过期(存在且没有过期直接返回true)不进行反复登录请求
+		if ((await useIsExpiration()) && uni.getStorageSync('token')) {
+			resolve(true);
+			return;
+		}
+		uni.login({
+			success(res) {
+				uni.request({
+					url:
+						REQUEST_CONFIG.BASE_URL +
+						'wsgw/' +
+						app.globalData.channelId +
+						'/' +
+						app.globalData.configKey +
+						'/' +
+						app.globalData.hosId +
+						'/smallProgramLogin_v2.do?cfgKey=' +
+						app.globalData.wechatConfigKey,
+					method: 'POST',
+					data: {
+						appId: app.globalData.appId,
+						smallPro_authCode: res.code,
+						smallPro_systemInfo: app.globalData.smallPro_systemInfo,
+						smallPro_version: app.globalData.smallPro_version,
+					},
+					header: {
+						'content-type': 'application/x-www-form-urlencoded',
+					},
+					success(resp: any) {
+						if (resp.data.RespCode == '10000') {
+							uni.setStorageSync('token', resp.data.token);
+							getApp().globalData.token = resp.data.token;
+							uni.setStorageSync('openid', resp.data.openId);
+							uni.setStorageSync('unionid', resp.data.unionId);
+							uni.setStorageSync('smallProOpenId', resp.data.smallProOpenId);
+							uni.setStorageSync('wechatOpenid', resp.data.wechatOpenid);
+							uni.setStorageSync('isCall', 0);
+
+							// publicFn.preserMember();
+							// 当前时间
+							var timestamp = Date.parse(new Date());
+							// 加上过期期限
+							var expiration = timestamp + resp.data.expireTime;
+							uni.setStorageSync('data_expiration', expiration);
+							resolve(resp);
+						} else {
+							if (
+								resp.data.RespCode == '-14019' &&
+								resp.data.RespMessage &&
+								resp.data.RespMessage.indexOf('非白名单用户') >= 0
+							) {
+								uni.showLoading({
+									title: '系统升级中...',
+									mask: true,
+								});
+							} else {
+								uni.showLoading({
+									title: resp.data.RespMessage || '网络异常!',
+									mask: true,
+								});
+							}
+							resolve(false);
+						}
+					},
+					fail(error) {
+						uni.showLoading({
+							title: '网络异常!',
+							mask: true,
+						});
+						reject(error);
+					},
+				});
+			},
+			fail(error) {
+				uni.showLoading({
+					title: '网络异常!',
+					mask: true,
+				});
+				reject(error);
+			},
+		});
+	});
+};
+
+const smallProgramLoginByGONGZHONGHAO = (app: App.AppInstance) => {
+	return new Promise(async (resolve, reject) => {
+		if ((await useIsExpiration()) && uni.getStorageSync('token')) {
+			resolve(true);
+			return;
+		}
+		const code =
+			new URL(location.href).searchParams.get('code') || uni.getStorageSync('login_code');
+		if (!code) {
+			// 没有授权登录过的,需要重定向到授权页面
+			location.href = WebOauth2Authorize({
+				appid: app.globalData.appId,
+				redirect_uri: encodeURIComponent(`${useDomain()}/#/`),
+			});
+		} else {
+			uni.setStorageSync('login_code', code);
+			location.href = `${useDomain()}/#/`;
+			const { channelId, configKey, hosId, wechatConfigKey } = app.globalData;
+			uni.request({
+				url: `${REQUEST_CONFIG.BASE_URL}wsgw/${channelId}/${configKey}/${hosId}/smallProgramLogin_v2.do?cfgKey=${wechatConfigKey}`,
+				method: 'POST',
+				data: {
+					appId: app.globalData.appId,
+					smallPro_authCode: code,
+					smallPro_systemInfo: app.globalData.smallPro_systemInfo,
+					smallPro_version: app.globalData.smallPro_version,
+				},
+				header: {
+					'content-type': 'application/x-www-form-urlencoded',
+				},
+				success(resp: any) {
+					console.log('1111', resp);
+					if (resp.data.RespCode == '10000') {
+						uni.setStorageSync('token', resp.data.token);
+						getApp().globalData.token = resp.data.token;
+						uni.setStorageSync('openid', resp.data.openId);
+						uni.setStorageSync('unionid', resp.data.unionId);
+						uni.setStorageSync('smallProOpenId', resp.data.smallProOpenId);
+						uni.setStorageSync('wechatOpenid', resp.data.wechatOpenid);
+						uni.setStorageSync('isCall', 0);
+
+						// publicFn.preserMember();
+						// 当前时间
+						var timestamp = Date.parse(new Date());
+						// 加上过期期限
+						var expiration = timestamp + resp.data.expireTime;
+						uni.setStorageSync('data_expiration', expiration);
+						resolve(resp);
+					} else {
+						if (
+							resp.data.RespCode == '-14019' &&
+							resp.data.RespMessage &&
+							resp.data.RespMessage.indexOf('非白名单用户') >= 0
+						) {
+							uni.showLoading({
+								title: '系统升级中...',
+								mask: true,
+							});
+						} else {
+							uni.showLoading({
+								title: resp.data.RespMessage || '网络异常!',
+								mask: true,
+							});
+						}
+						resolve(false);
+					}
+				},
+				fail(error) {
+					console.log(error);
+					uni.showLoading({
+						title: '网络异常!',
+						mask: true,
+					});
+					reject(error);
+				},
+			});
+		}
+	});
+};

+ 3 - 0
index.ts

@@ -0,0 +1,3 @@
+export * from './hook';
+export * from './service';
+export * from './utils';

+ 14 - 0
package.json

@@ -0,0 +1,14 @@
+{
+	"name": "@kasite/uni-app-base",
+	"version": "0.0.1",
+	"main": "index.ts",
+	"private": false,
+	"description": "uni-app base code",
+	"keywords": [
+		"kasite",
+		"uni-app"
+	],
+	"publishConfig": {
+		"registry": "http://maven.kasitesoft.com/repository/intelmt-npm-hosted/"
+	}
+}

+ 81 - 0
service/base/index.ts

@@ -0,0 +1,81 @@
+import request from '../request';
+import handle from '../handle';
+import { REQUEST_CONFIG } from '../../config';
+
+/**
+ * 跳转授权链接
+ * @param {String} 	appid						appid
+ * @param {Url}			redirect_uri		授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
+ * @param {String} 	response_type		返回类型,请填写code
+ * @param {String} 	scope						snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
+ */
+export const WebOauth2Authorize = (queryData: any) => {
+	return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${queryData.appid}&redirect_uri=${queryData.redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
+};
+
+/** 获取系统状态 */
+export const GetAppStatus = async (queryData: any) => {
+	const resp = handle.promistHandle(
+		await request.doPost(
+			`${REQUEST_CONFIG.BASE_URL}kstserver/status?appId=${queryData.appId}`,
+			{},
+			{
+				construction: true,
+			}
+		)
+	);
+	return handle.catchPromise(resp, () => resp);
+};
+
+/** 页面参数查询列表 */
+export const SysAppPageList = async (queryData: any) => {
+	const resp = handle.promistHandle(
+		await request.doPost(
+			`${REQUEST_CONFIG.BASE_URL}wsgw/sysAppPage/api/List/callApiJSON.do`,
+			queryData,
+			{
+				showLoading: false,
+			}
+		)
+	);
+	return handle.catchPromise(resp, () => resp);
+};
+
+/** 查询就诊人信息 */
+export const QueryBaseMemberList_V3 = async (queryData: any) => {
+	const resp = handle.promistHandleNew(
+		await request.doPost(
+			`${REQUEST_CONFIG.BASE_URL}wsgw/accountMember/api/QueryBaseMemberList_V3/callApiJSON.do`,
+			queryData
+		)
+	);
+	return handle.catchPromiseNew(resp, () => resp);
+};
+
+/** 获取表单配置信息 */
+export const BaseFormQuery = async (queryData: any) => {
+	let resp = handle.promistHandle(
+		await request.doPost(
+			`${REQUEST_CONFIG.BASE_URL}wsgw/base/form/Query/callApiJSON.do`,
+			queryData,
+			{
+				showLoading: false,
+			}
+		)
+	);
+	return handle.catchPromise(resp, () => resp);
+};
+
+/** 查询功能菜单列表 */
+export const SysclientappmenuList = async (queryData: any) => {
+	let resp = handle.promistHandle(
+		await request.doPost(
+			`${REQUEST_CONFIG.BASE_URL}wsgw/sysclientappmenu/api/List/callApiJSON.do`,
+			queryData,
+			{
+				showLoading: false,
+			}
+		)
+	);
+	return handle.catchPromise(resp, () => resp);
+};

+ 186 - 0
service/handle.js

@@ -0,0 +1,186 @@
+/**
+ * request 返回值检测
+ */
+import {
+	common
+} from "../utils";
+
+import {
+	createSSEParser
+} from "../utils/sseParse";
+
+const app = getApp()
+const promistHandle = res => {
+	// 前端判断返回的参数是不是加密数据,是的话先解密
+	if ((typeof res.data.data == 'string') && res.data.data.constructor == String) {
+		res.data = JSON.parse(common.desDecrypt(res.data.data, getApp().globalData.apiSecretKey));
+	}
+	//有些接口没有返回RespCode  通过resultCode判断
+	let resp = ""
+	if (res.data.RespCode == '10000' || res.data.resultCode == '0') {
+		// 请求成功 
+		resp = []
+		//getUnitePay  res.data.Data是个对象
+		if (res.data.Data instanceof Array || res.data.Data instanceof Object) {
+			resp = res.data.Data;
+		} else if (res.data.WxPayConfigKey || res.data.resultCode == '0' || res.data.configKey || res.data.result || res
+			.data.clientId) {
+			resp = res.data;
+		}
+	} else {
+		// 503 - 小程序下线、维护中,跳转到对应页面
+		if (res.data.RespCode == 503) {
+			jumpErrorPage()
+		}
+		//如果401 token失效  不处理 返回""
+		if (res.data.RespCode != '401') {
+			resp = res.data.RespMessage || res.data.errMsg;
+		}
+	}
+	return resp;
+}
+
+const catchPromise = (resp, callBack, showModal = true) => {
+	// 如果不是是object类型  说明是抛出的异常 ""是401  
+	if (resp === "") {
+		return false;
+	}
+	if (typeof resp != 'object') {
+
+		if (!showModal) {
+			return false;
+		}
+		common.showModal(resp)
+		return false;
+	} else {
+		// 否则执行回调
+		if (callBack) {
+			return callBack()
+		}
+	}
+}
+
+
+/**
+ * 新的处理方法 promistHandleNew 会暴露出res.data  方便医院个性化处理res.data.RespCode逻辑
+ * catchPromiseNew 添加提示报错信息后的点击确定回调方法
+ */
+
+/**
+ * * let resp = handle.promistHandleNew(await request.doPost(url, queryData))
+ * //showModal  报错是否提示   showModalCallBack 报错提示按钮点击的回调
+ *  return handle.catchPromiseNew(resp, () => resp,{showModal:true,showModalCallBack:()=>{}})
+ */
+const promistHandleNew = res => {
+	// 前端判断返回的参数是不是加密数据,是的话先解密
+	if ((typeof res.data.data == 'string') && res.data.data.constructor == String) {
+		res.data = JSON.parse(common.desDecrypt(res.data.data, getApp().globalData.apiSecretKey));
+	}
+	//有些接口没有返回RespCode  通过resultCode判断
+	let resp = ""
+	if (res.data.RespCode == '10000' || res.data.resultCode == '0') {
+		// 请求成功 
+		resp = []
+		//getUnitePay  res.data.Data是个对象
+		if (res.data.Data instanceof Array || res.data.Data instanceof Object) {
+			resp = res.data.Data;
+		} else if (res.data.WxPayConfigKey || res.data.resultCode == '0' || res.data.configKey || res.data.result || res
+			.data.clientId) {
+			resp = res.data;
+		}
+	} else {
+		// 503 - 小程序下线、维护中,跳转到对应页面
+		if (res.data.RespCode == 503) {
+			jumpErrorPage()
+		}
+		//如果401 token失效  不处理 返回""
+		if (res.data.RespCode != '401') {
+			resp = res.data.RespMessage || res.data.errMsg;
+		}
+	}
+	return {
+		resp,
+		resData: res.data
+	};
+}
+
+const catchPromiseNew = (data, callBack, option = {
+	showModal: true,
+	showModalCallBack: () => {}
+}) => {
+	// 如果不是是object类型  说明是抛出的异常 ""是401  
+	if (data.resp === "") {
+		return {
+			resp: false,
+			resData: data.resData
+		};
+	}
+	if (typeof data.resp != 'object') {
+		if (!option.showModal || data.resData.RespCode == 503) {
+			return {
+				resp: false,
+				resData: data.resData
+			};
+		}
+		common.showModal(data.resp, option.showModalCallBack)
+		return {
+			resp: false,
+			resData: data.resData
+		};
+	} else {
+		// 否则执行回调
+		if (callBack) {
+			return callBack()
+		}
+	}
+}
+
+const jumpErrorPage = () => {
+	const [currentPage] = getCurrentPages()
+	const maintainPage = "pages/st1/business/errorPage/maintain/maintain"
+	const offlinePage = "pages/st1/business/errorPage/offline/offline"
+	const isMaintainPage = currentPage.route.indexOf(maintainPage) > 0
+	const isOfflinePage = currentPage.route.indexOf(offlinePage) > 0
+	let jumpPage = ""
+	if (res.data.RespCode == "maintain" && !isMaintainPage) {
+		jumpPage = maintainPage
+	}
+	if (res.data.RespCode == "offline" && !isOfflinePage) {
+		jumpPage = offlinePage
+	}
+	jumpPage && uni.reLaunch({
+		url: `/${jumpPage}`,
+	})
+}
+
+function arrayBufferToString(arrayBuffer) {
+	// 创建一个 Uint8Array 视图
+	let binary = '';
+	const bytes = new Uint8Array(arrayBuffer);
+	const len = bytes.byteLength;
+	for (let i = 0; i < len; i++) {
+		binary += String.fromCharCode(bytes[i]);
+	}
+	return decodeURIComponent(escape(binary)); // 处理UTF-8编码
+}
+
+
+const sseChunkDataHandle = (onChunkReceived) => {
+	let ssePaser = createSSEParser(onChunkReceived);
+	return (res) => {
+		try {
+			let data = arrayBufferToString(res.data);
+			console.log("==>", data)
+			ssePaser.parse(data);
+		} catch (e) {
+			console.error(e)
+		}
+	}
+}
+export default {
+	promistHandle,
+	catchPromise,
+	promistHandleNew,
+	catchPromiseNew,
+	sseChunkDataHandle
+}

+ 2 - 0
service/index.ts

@@ -0,0 +1,2 @@
+export { default as request } from './request';
+export { default as handle } from './handle';

+ 277 - 0
service/request.ts

@@ -0,0 +1,277 @@
+import { common } from '../utils';
+import { useSmallProgramLogin } from '../hook';
+let _app;
+
+/**
+ * HTTP POST 请求封装
+ * complete:是否成功失败都往下执行
+ */
+const httpPost = (url: string, data: any, options: any = {}, complete = undefined) => {
+	let has = getApp().globalData.accountInfo.miniProgram.envVersion != 'develop'; // 如果不等于开发版本,默认启用请求加密
+	let querData: any = {};
+	// 判断当前请求接口不需要加密请求
+	if (common.isNotEmpty(getApp().globalData.noNeedSecretKeyApiArr)) {
+		for (let i of getApp().globalData.noNeedSecretKeyApiArr) {
+			if (url.indexOf(i) != '-1') {
+				has = false;
+				break;
+			}
+		}
+	}
+	// 新增判断请求的url 如果不存在callApiJSON,callApi.do请求,全部不加密(后续callApi.do接口前端也统一加密)
+	if (url.indexOf('callApiJSON') == -1 && url.indexOf('callApi.do') == -1) {
+		has = false;
+	}
+	let showLoading = true;
+	if (options != undefined && options.showLoading != undefined) {
+		showLoading = options.showLoading;
+	}
+	if (showLoading) {
+		common.showLoading();
+	}
+
+	let header: any = {
+		'content-type': 'application/json',
+		token: getApp().globalData.token || uni.getStorageSync('token'),
+		'call-appId': uni.getStorageSync('frontEndConfig').fixedAppjsGlobalData.appId,
+	};
+	// 如果has 为true 代表当前请求接口需要加密 且获取的加密秘钥不为空
+	if (has && common.isNotEmpty(getApp().globalData.apiSecretKey)) {
+		(header.appId = 'KASIET_alismaillpro'),
+			(querData.data = common.desEncrypt(JSON.stringify(data), getApp().globalData.apiSecretKey));
+	}
+	if (options) {
+		const extOptions = [
+			'showLoading', // 显示加载中
+			'onChunkReceived', // 分块传输监听
+			'timeout', // 超时时间, 默认取/app.json里的networkTimeout配置
+			'responseType', // 响应结果
+		];
+		Object.keys(options).forEach((key) => {
+			if (!extOptions.includes(key)) {
+				header[key] = options[key];
+			}
+		});
+	}
+	return new Promise((resolve, reject) => {
+		const enableChunked = options && typeof options.onChunkReceived == 'function';
+		const requestOptions: any = {
+			url: url,
+			data: common.isEmpty(querData) ? data : querData,
+			method: 'POST',
+			header: header,
+			enableChunked: enableChunked, // 新增分块数据
+			success(res) {
+				if (showLoading) {
+					common.hideLoading();
+				}
+				if (!complete) {
+					if (options && options.construction) {
+						resolve({ data: { Data: res.data, RespCode: 10000 } });
+					} else {
+						resolve(res);
+					}
+				}
+			},
+			fail(error) {
+				if (showLoading) {
+					common.hideLoading();
+				}
+				if (!complete) {
+					reject(error);
+				}
+				console.error(error);
+				common.showToast(`连接超时,请重试`);
+			},
+			complete(res) {
+				if (complete) {
+					resolve(res);
+				}
+			},
+		};
+		if (options && options.timeout != undefined && options.timeout != null) {
+			requestOptions.timeout = options.timeout;
+		}
+		if (options && options.responseType != undefined && options.responseType != null) {
+			requestOptions.responseType = options.responseType;
+		}
+		const wxRequest = uni.request(requestOptions);
+		if (enableChunked) {
+			wxRequest.onChunkReceived(options.onChunkReceived);
+		}
+	});
+};
+
+/**
+ * 微信请求post方法封装
+ * url
+ * data 以对象的格式传入
+ * 后端返回 401 token超时时,会重新发起调用登陆
+ */
+const doPost = async (url: string, data: any, options: any = {}, complete = undefined) => {
+	const response = httpPost(url, data, options, complete);
+	let loginAgain = false;
+	await response.then((resp: any) => {
+		if (resp.data.RespCode == 401) {
+			loginAgain = true;
+		}
+	});
+	// token未超时,直接返回
+	if (!loginAgain) {
+		return response;
+	}
+	// token超时时,调用接口重新登录
+	if (uni.getStorageSync('isCall') != 1) {
+		// 防止页面并发调用登录接口
+		uni.setStorageSync('isCall', 1);
+		uni.setStorageSync('data_expiration', 0);
+		// 获取登录失败轮询次数,,默认5次
+		let retry = getApp().globalData.retryNum;
+		while (retry > 0) {
+			await useSmallProgramLogin(_app).then((resp: any) => {
+				if (resp.data.RespCode != '10000') {
+					// 登录失败,需轮询,次数减一
+					retry--;
+				} else {
+					// 登录成功
+					retry = 0;
+					// uni.clearStorageSync()
+				}
+			});
+			if (retry != 0) {
+				// 需要重试时,先休眠300毫秒
+				await sleep(300);
+			}
+		}
+	}
+	// 接口调用重试次数,默认判断5次,每次间隔500毫秒
+	let retry = getApp().globalData.retryNum;
+	while (retry > 0) {
+		if (uni.getStorageSync('isCall') != 1) {
+			// 如果已经重新登录成功,返回重新请求接口结果
+			return httpPost(url, data, options);
+		} else {
+			// 还未登录成功,休眠500毫秒再判断
+			await sleep(500);
+			retry--;
+		}
+	}
+	// 重试5次后,再次重新发起请求
+	return httpPost(url, data, options);
+};
+
+/**
+ * 微信请求get方法封装
+ * url
+ * data 以对象的格式传入
+ * 后端返回 401 token超时时,会重新发起调用登陆
+ */
+const doGet = async (url: string, data: any, options: any = {}) => {
+	const response = httpGet(url, data, options);
+	let loginAgain = false;
+	await response.then((resp: any) => {
+		if (resp.data.RespCode == 401) {
+			loginAgain = true;
+		}
+	});
+	// token未超时,直接返回
+	if (!loginAgain) {
+		return response;
+	}
+	// token超时时,调用接口重新登录
+	if (uni.getStorageSync('isCall') != 1) {
+		// 防止页面并发调用登录接口
+		uni.setStorageSync('isCall', 1);
+		uni.setStorageSync('data_expiration', 0);
+		// 获取登录失败轮询次数,默认5次
+		let retry = getApp().globalData.retryNum;
+		while (retry > 0) {
+			await useSmallProgramLogin(_app).then((resp: any) => {
+				if (resp.data.RespCode != '10000') {
+					// 登录失败,需轮询,次数减一
+					retry--;
+				} else {
+					// 登录成功
+					retry = 0;
+				}
+			});
+			if (retry != 0) {
+				// 需要重试时,先休眠300毫秒
+				await sleep(300);
+			}
+		}
+	}
+	// 接口调用重试次数,默认判断5次,每次间隔500毫秒
+	let retry = getApp().globalData.retryNum;
+	while (retry > 0) {
+		if (uni.getStorageSync('isCall') != 1) {
+			// 如果已经重新登录成功,返回重新请求接口结果
+			return httpGet(url, data, options);
+		} else {
+			// 还未登录成功,休眠500毫秒再判断
+			await sleep(500);
+			retry--;
+		}
+	}
+	// 重试5次后,再次重新发起请求
+	return httpGet(url, data, options);
+};
+
+// 新增 httpGet 基础方法
+const httpGet = (url: string, data: any, options: any = {}) => {
+	let showLoading = true;
+	if (options != undefined && options.showLoading != undefined) {
+		showLoading = options.showLoading;
+	}
+	if (showLoading) {
+		common.showLoading();
+	}
+
+	let header = {
+		'content-type': 'application/json',
+		token: getApp().globalData.token || uni.getStorageSync('token'),
+		'call-appId': uni.getStorageSync('frontEndConfig').fixedAppjsGlobalData.appId,
+	};
+
+	if (options) {
+		Object.keys(options).forEach((key) => {
+			if (key != 'showLoading') {
+				header[key] = options[key];
+			}
+		});
+	}
+
+	return new Promise((resolve, reject) => {
+		uni.request({
+			url: url,
+			data: data,
+			method: 'GET',
+			header: header,
+			success(res) {
+				if (showLoading) {
+					common.hideLoading();
+				}
+				if (options && options.construction) {
+					resolve({ data: { Data: res.data, RespCode: 10000 } });
+				} else {
+					resolve(res);
+				}
+			},
+			fail(error) {
+				if (showLoading) {
+					common.hideLoading();
+				}
+				reject(error);
+				common.showToast(`连接超时,请重试`);
+			},
+		});
+	});
+};
+
+//模拟线程休眠,需配合 async和await使用
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+export default {
+	doPost,
+	doGet,
+};

+ 116 - 0
utils/common.ts

@@ -0,0 +1,116 @@
+// import { CryptoJS } from './crypto-js.min.js';
+import { CryptoJS } from './crypto-js.min.js';
+
+let loading = false;
+let count = 0;
+
+/** 请求弹窗开启 */
+export const showLoading = function (title = '') {
+	if (!loading) {
+		uni.showLoading({
+			title: title || '',
+			mask: true,
+		});
+		loading = true;
+	}
+	count += 1;
+};
+/** 请求弹窗关闭 */
+export const hideLoading = function () {
+	let timer = setTimeout(() => {
+		// 在下一次tick中如果没有新的loading进来(count === 1)  则关闭loading (消除多接口同步或者异步的loading闪屏)
+		if (count === 1 && loading) {
+			uni.hideLoading();
+			loading = false;
+		}
+		count -= 1;
+		clearTimeout(timer);
+		timer = null;
+	}, 0);
+};
+/** 提示消息 */
+export const showToast = function (msg: string, fn = () => {}, duration = 1500) {
+	let timer = null;
+	uni.showToast({
+		title: msg,
+		mask: true,
+		icon: 'none',
+		duration: duration,
+	});
+	if (fn) {
+		timer = setTimeout(() => {
+			fn();
+			timer = null;
+		}, duration);
+	}
+};
+/** 提示信息 */
+export const showModal = function (content: string, callBack = () => {}, data: any = {}) {
+	uni.showModal({
+		content: content,
+		title: data.title || '',
+		confirmText: data.confirmText || '确定',
+		confirmColor: data.confirmColor || '',
+		cancelColor: data.cancelColor || '',
+		cancelText: data.cancelText || '',
+		showCancel: data.cancelText ? true : false,
+		success: (res) => {
+			if (res.confirm && callBack instanceof Function) {
+				callBack();
+			}
+			if (!res.confirm && data.callBack instanceof Function) {
+				data.callBack();
+			}
+		},
+	});
+};
+/** 是否为空 */
+export const isEmpty = function (obj) {
+	if (obj == undefined || obj == null || obj == '') {
+		return true;
+	} else if (obj instanceof Array) {
+		return obj.length <= 0;
+	} else if (obj instanceof Object) {
+		return Object.keys(obj).length <= 0;
+	}
+	return false;
+};
+/** 是否不为空 */
+export const isNotEmpty = function (obj) {
+	return !isEmpty(obj);
+};
+/** des加密 */
+export const desEncrypt = function (message, key) {
+	let keyHex = CryptoJS.enc.Utf8.parse(key);
+	let encrypted = CryptoJS.DES.encrypt(message, keyHex, {
+		mode: CryptoJS.mode.ECB,
+		padding: CryptoJS.pad.Pkcs7,
+	});
+	return encrypted.toString();
+};
+/** des ECB解密str:秘钥串;key:秘钥;exportType:输出方式; */
+export const desDecrypt = function (str, key, exportType) {
+	let keyHex = CryptoJS.enc.Utf8.parse(key);
+	let decrypted = CryptoJS.DES.decrypt(
+		exportType == 'hex'
+			? {
+					ciphertext: CryptoJS.enc.Hex.parse(str),
+			  }
+			: str,
+		keyHex,
+		{
+			mode: CryptoJS.mode.ECB,
+			padding: CryptoJS.pad.Pkcs7,
+		}
+	);
+
+	return decrypted.toString(CryptoJS.enc.Utf8);
+};
+/** 数组转化成对象,方便使用 */
+export const turnToMap = (list) => {
+	let map = {};
+	list.map((item) => {
+		map[item.routePath] = JSON.parse(item.infoJson || '{}');
+	});
+	return map;
+};

+ 1270 - 0
utils/crypto-js.min.js

@@ -0,0 +1,1270 @@
+export var CryptoJS = CryptoJS || function(u, l) {
+	var d = {},
+		n = d.lib = {},
+		p = function() {},
+		s = n.Base = {
+			extend: function(a) {
+				p.prototype = this;
+				var c = new p;
+				a && c.mixIn(a);
+				c.hasOwnProperty("init") || (c.init = function() {
+					c.$super.init.apply(this, arguments)
+				});
+				c.init.prototype = c;
+				c.$super = this;
+				return c
+			},
+			create: function() {
+				var a = this.extend();
+				a.init.apply(a, arguments);
+				return a
+			},
+			init: function() {},
+			mixIn: function(a) {
+				for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
+				a.hasOwnProperty("toString") && (this.toString = a.toString)
+			},
+			clone: function() {
+				return this.init.prototype.extend(this)
+			}
+		},
+		q = n.WordArray = s.extend({
+			init: function(a, c) {
+				a = this.words = a || [];
+				this.sigBytes = c != l ? c : 4 * a.length
+			},
+			toString: function(a) {
+				return (a || v).stringify(this)
+			},
+			concat: function(a) {
+				var c = this.words,
+					m = a.words,
+					f = this.sigBytes;
+				a = a.sigBytes;
+				this.clamp();
+				if (f % 4)
+					for (var t = 0; t < a; t++) c[f + t >>> 2] |= (m[t >>> 2] >>> 24 - 8 * (t % 4) & 255) << 24 - 8 * ((f +
+						t) % 4);
+				else if (65535 < m.length)
+					for (t = 0; t < a; t += 4) c[f + t >>> 2] = m[t >>> 2];
+				else c.push.apply(c, m);
+				this.sigBytes += a;
+				return this
+			},
+			clamp: function() {
+				var a = this.words,
+					c = this.sigBytes;
+				a[c >>> 2] &= 4294967295 <<
+					32 - 8 * (c % 4);
+				a.length = u.ceil(c / 4)
+			},
+			clone: function() {
+				var a = s.clone.call(this);
+				a.words = this.words.slice(0);
+				return a
+			},
+			random: function(a) {
+				for (var c = [], m = 0; m < a; m += 4) c.push(4294967296 * u.random() | 0);
+				return new q.init(c, a)
+			}
+		}),
+		w = d.enc = {},
+		v = w.Hex = {
+			stringify: function(a) {
+				var c = a.words;
+				a = a.sigBytes;
+				for (var m = [], f = 0; f < a; f++) {
+					var t = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;
+					m.push((t >>> 4).toString(16));
+					m.push((t & 15).toString(16))
+				}
+				return m.join("")
+			},
+			parse: function(a) {
+				for (var c = a.length, m = [], f = 0; f < c; f += 2) m[f >>> 3] |= parseInt(a.substr(f,
+					2), 16) << 24 - 4 * (f % 8);
+				return new q.init(m, c / 2)
+			}
+		},
+		b = w.Latin1 = {
+			stringify: function(a) {
+				var c = a.words;
+				a = a.sigBytes;
+				for (var m = [], f = 0; f < a; f++) m.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));
+				return m.join("")
+			},
+			parse: function(a) {
+				for (var c = a.length, m = [], f = 0; f < c; f++) m[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);
+				return new q.init(m, c)
+			}
+		},
+		x = w.Utf8 = {
+			stringify: function(a) {
+				try {
+					return decodeURIComponent(escape(b.stringify(a)))
+				} catch (c) {
+					throw Error("Malformed UTF-8 data");
+				}
+			},
+			parse: function(a) {
+				return b.parse(unescape(encodeURIComponent(a)))
+			}
+		},
+		r = n.BufferedBlockAlgorithm = s.extend({
+			reset: function() {
+				this._data = new q.init;
+				this._nDataBytes = 0
+			},
+			_append: function(a) {
+				"string" == typeof a && (a = x.parse(a));
+				this._data.concat(a);
+				this._nDataBytes += a.sigBytes
+			},
+			_process: function(a) {
+				var c = this._data,
+					m = c.words,
+					f = c.sigBytes,
+					t = this.blockSize,
+					b = f / (4 * t),
+					b = a ? u.ceil(b) : u.max((b | 0) - this._minBufferSize, 0);
+				a = b * t;
+				f = u.min(4 * a, f);
+				if (a) {
+					for (var e = 0; e < a; e += t) this._doProcessBlock(m, e);
+					e = m.splice(0, a);
+					c.sigBytes -= f
+				}
+				return new q.init(e, f)
+			},
+			clone: function() {
+				var a = s.clone.call(this);
+				a._data = this._data.clone();
+				return a
+			},
+			_minBufferSize: 0
+		});
+	n.Hasher = r.extend({
+		cfg: s.extend(),
+		init: function(a) {
+			this.cfg = this.cfg.extend(a);
+			this.reset()
+		},
+		reset: function() {
+			r.reset.call(this);
+			this._doReset()
+		},
+		update: function(a) {
+			this._append(a);
+			this._process();
+			return this
+		},
+		finalize: function(a) {
+			a && this._append(a);
+			return this._doFinalize()
+		},
+		blockSize: 16,
+		_createHelper: function(a) {
+			return function(c, m) {
+				return (new a.init(m)).finalize(c)
+			}
+		},
+		_createHmacHelper: function(a) {
+			return function(c, m) {
+				return (new e.HMAC.init(a,
+					m)).finalize(c)
+			}
+		}
+	});
+	var e = d.algo = {};
+	return d
+}(Math);
+(function() {
+	var u = CryptoJS,
+		l = u.lib.WordArray;
+	u.enc.Base64 = {
+		stringify: function(d) {
+			var n = d.words,
+				l = d.sigBytes,
+				s = this._map;
+			d.clamp();
+			d = [];
+			for (var q = 0; q < l; q += 3)
+				for (var w = (n[q >>> 2] >>> 24 - 8 * (q % 4) & 255) << 16 | (n[q + 1 >>> 2] >>> 24 - 8 * ((q + 1) % 4) &
+						255) << 8 | n[q + 2 >>> 2] >>> 24 - 8 * ((q + 2) % 4) & 255, v = 0; 4 > v && q + 0.75 * v < l; v++) d
+					.push(s.charAt(w >>> 6 * (3 - v) & 63));
+			if (n = s.charAt(64))
+				for (; d.length % 4;) d.push(n);
+			return d.join("")
+		},
+		parse: function(d) {
+			var n = d.length,
+				p = this._map,
+				s = p.charAt(64);
+			s && (s = d.indexOf(s), -1 != s && (n = s));
+			for (var s = [], q = 0, w = 0; w <
+				n; w++)
+				if (w % 4) {
+					var v = p.indexOf(d.charAt(w - 1)) << 2 * (w % 4),
+						b = p.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4);
+					s[q >>> 2] |= (v | b) << 24 - 8 * (q % 4);
+					q++
+				} return l.create(s, q)
+		},
+		_map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+	}
+})();
+(function(u) {
+	function l(b, e, a, c, m, f, t) {
+		b = b + (e & a | ~e & c) + m + t;
+		return (b << f | b >>> 32 - f) + e
+	}
+
+	function d(b, e, a, c, m, f, t) {
+		b = b + (e & c | a & ~c) + m + t;
+		return (b << f | b >>> 32 - f) + e
+	}
+
+	function n(b, e, a, c, m, f, t) {
+		b = b + (e ^ a ^ c) + m + t;
+		return (b << f | b >>> 32 - f) + e
+	}
+
+	function p(b, e, a, c, m, f, t) {
+		b = b + (a ^ (e | ~c)) + m + t;
+		return (b << f | b >>> 32 - f) + e
+	}
+	for (var s = CryptoJS, q = s.lib, w = q.WordArray, v = q.Hasher, q = s.algo, b = [], x = 0; 64 > x; x++) b[x] =
+		4294967296 * u.abs(u.sin(x + 1)) | 0;
+	q = q.MD5 = v.extend({
+		_doReset: function() {
+			this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878])
+		},
+		_doProcessBlock: function(r, e) {
+			for (var a = 0; 16 > a; a++) {
+				var c = e + a,
+					m = r[c];
+				r[c] = (m << 8 | m >>> 24) & 16711935 | (m << 24 | m >>> 8) & 4278255360
+			}
+			var a = this._hash.words,
+				c = r[e + 0],
+				m = r[e + 1],
+				f = r[e + 2],
+				t = r[e + 3],
+				y = r[e + 4],
+				q = r[e + 5],
+				s = r[e + 6],
+				w = r[e + 7],
+				v = r[e + 8],
+				u = r[e + 9],
+				x = r[e + 10],
+				z = r[e + 11],
+				A = r[e + 12],
+				B = r[e + 13],
+				C = r[e + 14],
+				D = r[e + 15],
+				g = a[0],
+				h = a[1],
+				j = a[2],
+				k = a[3],
+				g = l(g, h, j, k, c, 7, b[0]),
+				k = l(k, g, h, j, m, 12, b[1]),
+				j = l(j, k, g, h, f, 17, b[2]),
+				h = l(h, j, k, g, t, 22, b[3]),
+				g = l(g, h, j, k, y, 7, b[4]),
+				k = l(k, g, h, j, q, 12, b[5]),
+				j = l(j, k, g, h, s, 17, b[6]),
+				h = l(h, j, k, g, w, 22, b[7]),
+				g = l(g, h, j, k, v, 7, b[8]),
+				k = l(k, g, h, j, u, 12, b[9]),
+				j = l(j, k, g, h, x, 17, b[10]),
+				h = l(h, j, k, g, z, 22, b[11]),
+				g = l(g, h, j, k, A, 7, b[12]),
+				k = l(k, g, h, j, B, 12, b[13]),
+				j = l(j, k, g, h, C, 17, b[14]),
+				h = l(h, j, k, g, D, 22, b[15]),
+				g = d(g, h, j, k, m, 5, b[16]),
+				k = d(k, g, h, j, s, 9, b[17]),
+				j = d(j, k, g, h, z, 14, b[18]),
+				h = d(h, j, k, g, c, 20, b[19]),
+				g = d(g, h, j, k, q, 5, b[20]),
+				k = d(k, g, h, j, x, 9, b[21]),
+				j = d(j, k, g, h, D, 14, b[22]),
+				h = d(h, j, k, g, y, 20, b[23]),
+				g = d(g, h, j, k, u, 5, b[24]),
+				k = d(k, g, h, j, C, 9, b[25]),
+				j = d(j, k, g, h, t, 14, b[26]),
+				h = d(h, j, k, g, v, 20, b[27]),
+				g = d(g, h, j, k, B, 5, b[28]),
+				k = d(k, g,
+					h, j, f, 9, b[29]),
+				j = d(j, k, g, h, w, 14, b[30]),
+				h = d(h, j, k, g, A, 20, b[31]),
+				g = n(g, h, j, k, q, 4, b[32]),
+				k = n(k, g, h, j, v, 11, b[33]),
+				j = n(j, k, g, h, z, 16, b[34]),
+				h = n(h, j, k, g, C, 23, b[35]),
+				g = n(g, h, j, k, m, 4, b[36]),
+				k = n(k, g, h, j, y, 11, b[37]),
+				j = n(j, k, g, h, w, 16, b[38]),
+				h = n(h, j, k, g, x, 23, b[39]),
+				g = n(g, h, j, k, B, 4, b[40]),
+				k = n(k, g, h, j, c, 11, b[41]),
+				j = n(j, k, g, h, t, 16, b[42]),
+				h = n(h, j, k, g, s, 23, b[43]),
+				g = n(g, h, j, k, u, 4, b[44]),
+				k = n(k, g, h, j, A, 11, b[45]),
+				j = n(j, k, g, h, D, 16, b[46]),
+				h = n(h, j, k, g, f, 23, b[47]),
+				g = p(g, h, j, k, c, 6, b[48]),
+				k = p(k, g, h, j, w, 10, b[49]),
+				j = p(j, k, g, h,
+					C, 15, b[50]),
+				h = p(h, j, k, g, q, 21, b[51]),
+				g = p(g, h, j, k, A, 6, b[52]),
+				k = p(k, g, h, j, t, 10, b[53]),
+				j = p(j, k, g, h, x, 15, b[54]),
+				h = p(h, j, k, g, m, 21, b[55]),
+				g = p(g, h, j, k, v, 6, b[56]),
+				k = p(k, g, h, j, D, 10, b[57]),
+				j = p(j, k, g, h, s, 15, b[58]),
+				h = p(h, j, k, g, B, 21, b[59]),
+				g = p(g, h, j, k, y, 6, b[60]),
+				k = p(k, g, h, j, z, 10, b[61]),
+				j = p(j, k, g, h, f, 15, b[62]),
+				h = p(h, j, k, g, u, 21, b[63]);
+			a[0] = a[0] + g | 0;
+			a[1] = a[1] + h | 0;
+			a[2] = a[2] + j | 0;
+			a[3] = a[3] + k | 0
+		},
+		_doFinalize: function() {
+			var b = this._data,
+				e = b.words,
+				a = 8 * this._nDataBytes,
+				c = 8 * b.sigBytes;
+			e[c >>> 5] |= 128 << 24 - c % 32;
+			var m = u.floor(a /
+				4294967296);
+			e[(c + 64 >>> 9 << 4) + 15] = (m << 8 | m >>> 24) & 16711935 | (m << 24 | m >>> 8) & 4278255360;
+			e[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360;
+			b.sigBytes = 4 * (e.length + 1);
+			this._process();
+			b = this._hash;
+			e = b.words;
+			for (a = 0; 4 > a; a++) c = e[a], e[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) &
+				4278255360;
+			return b
+		},
+		clone: function() {
+			var b = v.clone.call(this);
+			b._hash = this._hash.clone();
+			return b
+		}
+	});
+	s.MD5 = v._createHelper(q);
+	s.HmacMD5 = v._createHmacHelper(q)
+})(Math);
+(function() {
+	var u = CryptoJS,
+		l = u.lib,
+		d = l.Base,
+		n = l.WordArray,
+		l = u.algo,
+		p = l.EvpKDF = d.extend({
+			cfg: d.extend({
+				keySize: 4,
+				hasher: l.MD5,
+				iterations: 1
+			}),
+			init: function(d) {
+				this.cfg = this.cfg.extend(d)
+			},
+			compute: function(d, l) {
+				for (var p = this.cfg, v = p.hasher.create(), b = n.create(), u = b.words, r = p.keySize, p = p
+						.iterations; u.length < r;) {
+					e && v.update(e);
+					var e = v.update(d).finalize(l);
+					v.reset();
+					for (var a = 1; a < p; a++) e = v.finalize(e), v.reset();
+					b.concat(e)
+				}
+				b.sigBytes = 4 * r;
+				return b
+			}
+		});
+	u.EvpKDF = function(d, l, n) {
+		return p.create(n).compute(d,
+			l)
+	}
+})();
+CryptoJS.lib.Cipher || function(u) {
+	var l = CryptoJS,
+		d = l.lib,
+		n = d.Base,
+		p = d.WordArray,
+		s = d.BufferedBlockAlgorithm,
+		q = l.enc.Base64,
+		w = l.algo.EvpKDF,
+		v = d.Cipher = s.extend({
+			cfg: n.extend(),
+			createEncryptor: function(m, a) {
+				return this.create(this._ENC_XFORM_MODE, m, a)
+			},
+			createDecryptor: function(m, a) {
+				return this.create(this._DEC_XFORM_MODE, m, a)
+			},
+			init: function(m, a, b) {
+				this.cfg = this.cfg.extend(b);
+				this._xformMode = m;
+				this._key = a;
+				this.reset()
+			},
+			reset: function() {
+				s.reset.call(this);
+				this._doReset()
+			},
+			process: function(a) {
+				this._append(a);
+				return this._process()
+			},
+			finalize: function(a) {
+				a && this._append(a);
+				return this._doFinalize()
+			},
+			keySize: 4,
+			ivSize: 4,
+			_ENC_XFORM_MODE: 1,
+			_DEC_XFORM_MODE: 2,
+			_createHelper: function(m) {
+				return {
+					encrypt: function(f, b, e) {
+						return ("string" == typeof b ? c : a).encrypt(m, f, b, e)
+					},
+					decrypt: function(f, b, e) {
+						return ("string" == typeof b ? c : a).decrypt(m, f, b, e)
+					}
+				}
+			}
+		});
+	d.StreamCipher = v.extend({
+		_doFinalize: function() {
+			return this._process(!0)
+		},
+		blockSize: 1
+	});
+	var b = l.mode = {},
+		x = function(a, f, b) {
+			var c = this._iv;
+			c ? this._iv = u : c = this._prevBlock;
+			for (var e = 0; e < b; e++) a[f + e] ^=
+				c[e]
+		},
+		r = (d.BlockCipherMode = n.extend({
+			createEncryptor: function(a, f) {
+				return this.Encryptor.create(a, f)
+			},
+			createDecryptor: function(a, f) {
+				return this.Decryptor.create(a, f)
+			},
+			init: function(a, f) {
+				this._cipher = a;
+				this._iv = f
+			}
+		})).extend();
+	r.Encryptor = r.extend({
+		processBlock: function(a, f) {
+			var b = this._cipher,
+				c = b.blockSize;
+			x.call(this, a, f, c);
+			b.encryptBlock(a, f);
+			this._prevBlock = a.slice(f, f + c)
+		}
+	});
+	r.Decryptor = r.extend({
+		processBlock: function(a, b) {
+			var c = this._cipher,
+				e = c.blockSize,
+				d = a.slice(b, b + e);
+			c.decryptBlock(a, b);
+			x.call(this,
+				a, b, e);
+			this._prevBlock = d
+		}
+	});
+	b = b.CBC = r;
+	r = (l.pad = {}).Pkcs7 = {
+		pad: function(a, b) {
+			for (var c = 4 * b, c = c - a.sigBytes % c, e = c << 24 | c << 16 | c << 8 | c, d = [], l = 0; l < c; l +=
+				4) d.push(e);
+			c = p.create(d, c);
+			a.concat(c)
+		},
+		unpad: function(a) {
+			a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255
+		}
+	};
+	d.BlockCipher = v.extend({
+		cfg: v.cfg.extend({
+			mode: b,
+			padding: r
+		}),
+		reset: function() {
+			v.reset.call(this);
+			var a = this.cfg,
+				c = a.iv,
+				a = a.mode;
+			if (this._xformMode == this._ENC_XFORM_MODE) var b = a.createEncryptor;
+			else b = a.createDecryptor, this._minBufferSize = 1;
+			this._mode = b.call(a,
+				this, c && c.words)
+		},
+		_doProcessBlock: function(a, c) {
+			this._mode.processBlock(a, c)
+		},
+		_doFinalize: function() {
+			var a = this.cfg.padding;
+			if (this._xformMode == this._ENC_XFORM_MODE) {
+				a.pad(this._data, this.blockSize);
+				var c = this._process(!0)
+			} else c = this._process(!0), a.unpad(c);
+			return c
+		},
+		blockSize: 4
+	});
+	var e = d.CipherParams = n.extend({
+			init: function(a) {
+				this.mixIn(a)
+			},
+			toString: function(a) {
+				return (a || this.formatter).stringify(this)
+			}
+		}),
+		b = (l.format = {}).OpenSSL = {
+			stringify: function(a) {
+				var c = a.ciphertext;
+				a = a.salt;
+				return (a ? p.create([1398893684,
+					1701076831
+				]).concat(a).concat(c) : c).toString(q)
+			},
+			parse: function(a) {
+				a = q.parse(a);
+				var c = a.words;
+				if (1398893684 == c[0] && 1701076831 == c[1]) {
+					var b = p.create(c.slice(2, 4));
+					c.splice(0, 4);
+					a.sigBytes -= 16
+				}
+				return e.create({
+					ciphertext: a,
+					salt: b
+				})
+			}
+		},
+		a = d.SerializableCipher = n.extend({
+			cfg: n.extend({
+				format: b
+			}),
+			encrypt: function(a, c, b, d) {
+				d = this.cfg.extend(d);
+				var l = a.createEncryptor(b, d);
+				c = l.finalize(c);
+				l = l.cfg;
+				return e.create({
+					ciphertext: c,
+					key: b,
+					iv: l.iv,
+					algorithm: a,
+					mode: l.mode,
+					padding: l.padding,
+					blockSize: a.blockSize,
+					formatter: d.format
+				})
+			},
+			decrypt: function(a, c, b, e) {
+				e = this.cfg.extend(e);
+				c = this._parse(c, e.format);
+				return a.createDecryptor(b, e).finalize(c.ciphertext)
+			},
+			_parse: function(a, c) {
+				return "string" == typeof a ? c.parse(a, this) : a
+			}
+		}),
+		l = (l.kdf = {}).OpenSSL = {
+			execute: function(a, c, b, d) {
+				d || (d = p.random(8));
+				a = w.create({
+					keySize: c + b
+				}).compute(a, d);
+				b = p.create(a.words.slice(c), 4 * b);
+				a.sigBytes = 4 * c;
+				return e.create({
+					key: a,
+					iv: b,
+					salt: d
+				})
+			}
+		},
+		c = d.PasswordBasedCipher = a.extend({
+			cfg: a.cfg.extend({
+				kdf: l
+			}),
+			encrypt: function(c, b, e, d) {
+				d = this.cfg.extend(d);
+				e = d.kdf.execute(e,
+					c.keySize, c.ivSize);
+				d.iv = e.iv;
+				c = a.encrypt.call(this, c, b, e.key, d);
+				c.mixIn(e);
+				return c
+			},
+			decrypt: function(c, b, e, d) {
+				d = this.cfg.extend(d);
+				b = this._parse(b, d.format);
+				e = d.kdf.execute(e, c.keySize, c.ivSize, b.salt);
+				d.iv = e.iv;
+				return a.decrypt.call(this, c, b, e.key, d)
+			}
+		})
+}();
+(function() {
+	function u(b, a) {
+		var c = (this._lBlock >>> b ^ this._rBlock) & a;
+		this._rBlock ^= c;
+		this._lBlock ^= c << b
+	}
+
+	function l(b, a) {
+		var c = (this._rBlock >>> b ^ this._lBlock) & a;
+		this._lBlock ^= c;
+		this._rBlock ^= c << b
+	}
+	var d = CryptoJS,
+		n = d.lib,
+		p = n.WordArray,
+		n = n.BlockCipher,
+		s = d.algo,
+		q = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
+			63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
+		],
+		w = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47,
+			55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+		],
+		v = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28],
+		b = [{
+			"0": 8421888,
+			268435456: 32768,
+			536870912: 8421378,
+			805306368: 2,
+			1073741824: 512,
+			1342177280: 8421890,
+			1610612736: 8389122,
+			1879048192: 8388608,
+			2147483648: 514,
+			2415919104: 8389120,
+			2684354560: 33280,
+			2952790016: 8421376,
+			3221225472: 32770,
+			3489660928: 8388610,
+			3758096384: 0,
+			4026531840: 33282,
+			134217728: 0,
+			402653184: 8421890,
+			671088640: 33282,
+			939524096: 32768,
+			1207959552: 8421888,
+			1476395008: 512,
+			1744830464: 8421378,
+			2013265920: 2,
+			2281701376: 8389120,
+			2550136832: 33280,
+			2818572288: 8421376,
+			3087007744: 8389122,
+			3355443200: 8388610,
+			3623878656: 32770,
+			3892314112: 514,
+			4160749568: 8388608,
+			1: 32768,
+			268435457: 2,
+			536870913: 8421888,
+			805306369: 8388608,
+			1073741825: 8421378,
+			1342177281: 33280,
+			1610612737: 512,
+			1879048193: 8389122,
+			2147483649: 8421890,
+			2415919105: 8421376,
+			2684354561: 8388610,
+			2952790017: 33282,
+			3221225473: 514,
+			3489660929: 8389120,
+			3758096385: 32770,
+			4026531841: 0,
+			134217729: 8421890,
+			402653185: 8421376,
+			671088641: 8388608,
+			939524097: 512,
+			1207959553: 32768,
+			1476395009: 8388610,
+			1744830465: 2,
+			2013265921: 33282,
+			2281701377: 32770,
+			2550136833: 8389122,
+			2818572289: 514,
+			3087007745: 8421888,
+			3355443201: 8389120,
+			3623878657: 0,
+			3892314113: 33280,
+			4160749569: 8421378
+		}, {
+			"0": 1074282512,
+			16777216: 16384,
+			33554432: 524288,
+			50331648: 1074266128,
+			67108864: 1073741840,
+			83886080: 1074282496,
+			100663296: 1073758208,
+			117440512: 16,
+			134217728: 540672,
+			150994944: 1073758224,
+			167772160: 1073741824,
+			184549376: 540688,
+			201326592: 524304,
+			218103808: 0,
+			234881024: 16400,
+			251658240: 1074266112,
+			8388608: 1073758208,
+			25165824: 540688,
+			41943040: 16,
+			58720256: 1073758224,
+			75497472: 1074282512,
+			92274688: 1073741824,
+			109051904: 524288,
+			125829120: 1074266128,
+			142606336: 524304,
+			159383552: 0,
+			176160768: 16384,
+			192937984: 1074266112,
+			209715200: 1073741840,
+			226492416: 540672,
+			243269632: 1074282496,
+			260046848: 16400,
+			268435456: 0,
+			285212672: 1074266128,
+			301989888: 1073758224,
+			318767104: 1074282496,
+			335544320: 1074266112,
+			352321536: 16,
+			369098752: 540688,
+			385875968: 16384,
+			402653184: 16400,
+			419430400: 524288,
+			436207616: 524304,
+			452984832: 1073741840,
+			469762048: 540672,
+			486539264: 1073758208,
+			503316480: 1073741824,
+			520093696: 1074282512,
+			276824064: 540688,
+			293601280: 524288,
+			310378496: 1074266112,
+			327155712: 16384,
+			343932928: 1073758208,
+			360710144: 1074282512,
+			377487360: 16,
+			394264576: 1073741824,
+			411041792: 1074282496,
+			427819008: 1073741840,
+			444596224: 1073758224,
+			461373440: 524304,
+			478150656: 0,
+			494927872: 16400,
+			511705088: 1074266128,
+			528482304: 540672
+		}, {
+			"0": 260,
+			1048576: 0,
+			2097152: 67109120,
+			3145728: 65796,
+			4194304: 65540,
+			5242880: 67108868,
+			6291456: 67174660,
+			7340032: 67174400,
+			8388608: 67108864,
+			9437184: 67174656,
+			10485760: 65792,
+			11534336: 67174404,
+			12582912: 67109124,
+			13631488: 65536,
+			14680064: 4,
+			15728640: 256,
+			524288: 67174656,
+			1572864: 67174404,
+			2621440: 0,
+			3670016: 67109120,
+			4718592: 67108868,
+			5767168: 65536,
+			6815744: 65540,
+			7864320: 260,
+			8912896: 4,
+			9961472: 256,
+			11010048: 67174400,
+			12058624: 65796,
+			13107200: 65792,
+			14155776: 67109124,
+			15204352: 67174660,
+			16252928: 67108864,
+			16777216: 67174656,
+			17825792: 65540,
+			18874368: 65536,
+			19922944: 67109120,
+			20971520: 256,
+			22020096: 67174660,
+			23068672: 67108868,
+			24117248: 0,
+			25165824: 67109124,
+			26214400: 67108864,
+			27262976: 4,
+			28311552: 65792,
+			29360128: 67174400,
+			30408704: 260,
+			31457280: 65796,
+			32505856: 67174404,
+			17301504: 67108864,
+			18350080: 260,
+			19398656: 67174656,
+			20447232: 0,
+			21495808: 65540,
+			22544384: 67109120,
+			23592960: 256,
+			24641536: 67174404,
+			25690112: 65536,
+			26738688: 67174660,
+			27787264: 65796,
+			28835840: 67108868,
+			29884416: 67109124,
+			30932992: 67174400,
+			31981568: 4,
+			33030144: 65792
+		}, {
+			"0": 2151682048,
+			65536: 2147487808,
+			131072: 4198464,
+			196608: 2151677952,
+			262144: 0,
+			327680: 4198400,
+			393216: 2147483712,
+			458752: 4194368,
+			524288: 2147483648,
+			589824: 4194304,
+			655360: 64,
+			720896: 2147487744,
+			786432: 2151678016,
+			851968: 4160,
+			917504: 4096,
+			983040: 2151682112,
+			32768: 2147487808,
+			98304: 64,
+			163840: 2151678016,
+			229376: 2147487744,
+			294912: 4198400,
+			360448: 2151682112,
+			425984: 0,
+			491520: 2151677952,
+			557056: 4096,
+			622592: 2151682048,
+			688128: 4194304,
+			753664: 4160,
+			819200: 2147483648,
+			884736: 4194368,
+			950272: 4198464,
+			1015808: 2147483712,
+			1048576: 4194368,
+			1114112: 4198400,
+			1179648: 2147483712,
+			1245184: 0,
+			1310720: 4160,
+			1376256: 2151678016,
+			1441792: 2151682048,
+			1507328: 2147487808,
+			1572864: 2151682112,
+			1638400: 2147483648,
+			1703936: 2151677952,
+			1769472: 4198464,
+			1835008: 2147487744,
+			1900544: 4194304,
+			1966080: 64,
+			2031616: 4096,
+			1081344: 2151677952,
+			1146880: 2151682112,
+			1212416: 0,
+			1277952: 4198400,
+			1343488: 4194368,
+			1409024: 2147483648,
+			1474560: 2147487808,
+			1540096: 64,
+			1605632: 2147483712,
+			1671168: 4096,
+			1736704: 2147487744,
+			1802240: 2151678016,
+			1867776: 4160,
+			1933312: 2151682048,
+			1998848: 4194304,
+			2064384: 4198464
+		}, {
+			"0": 128,
+			4096: 17039360,
+			8192: 262144,
+			12288: 536870912,
+			16384: 537133184,
+			20480: 16777344,
+			24576: 553648256,
+			28672: 262272,
+			32768: 16777216,
+			36864: 537133056,
+			40960: 536871040,
+			45056: 553910400,
+			49152: 553910272,
+			53248: 0,
+			57344: 17039488,
+			61440: 553648128,
+			2048: 17039488,
+			6144: 553648256,
+			10240: 128,
+			14336: 17039360,
+			18432: 262144,
+			22528: 537133184,
+			26624: 553910272,
+			30720: 536870912,
+			34816: 537133056,
+			38912: 0,
+			43008: 553910400,
+			47104: 16777344,
+			51200: 536871040,
+			55296: 553648128,
+			59392: 16777216,
+			63488: 262272,
+			65536: 262144,
+			69632: 128,
+			73728: 536870912,
+			77824: 553648256,
+			81920: 16777344,
+			86016: 553910272,
+			90112: 537133184,
+			94208: 16777216,
+			98304: 553910400,
+			102400: 553648128,
+			106496: 17039360,
+			110592: 537133056,
+			114688: 262272,
+			118784: 536871040,
+			122880: 0,
+			126976: 17039488,
+			67584: 553648256,
+			71680: 16777216,
+			75776: 17039360,
+			79872: 537133184,
+			83968: 536870912,
+			88064: 17039488,
+			92160: 128,
+			96256: 553910272,
+			100352: 262272,
+			104448: 553910400,
+			108544: 0,
+			112640: 553648128,
+			116736: 16777344,
+			120832: 262144,
+			124928: 537133056,
+			129024: 536871040
+		}, {
+			"0": 268435464,
+			256: 8192,
+			512: 270532608,
+			768: 270540808,
+			1024: 268443648,
+			1280: 2097152,
+			1536: 2097160,
+			1792: 268435456,
+			2048: 0,
+			2304: 268443656,
+			2560: 2105344,
+			2816: 8,
+			3072: 270532616,
+			3328: 2105352,
+			3584: 8200,
+			3840: 270540800,
+			128: 270532608,
+			384: 270540808,
+			640: 8,
+			896: 2097152,
+			1152: 2105352,
+			1408: 268435464,
+			1664: 268443648,
+			1920: 8200,
+			2176: 2097160,
+			2432: 8192,
+			2688: 268443656,
+			2944: 270532616,
+			3200: 0,
+			3456: 270540800,
+			3712: 2105344,
+			3968: 268435456,
+			4096: 268443648,
+			4352: 270532616,
+			4608: 270540808,
+			4864: 8200,
+			5120: 2097152,
+			5376: 268435456,
+			5632: 268435464,
+			5888: 2105344,
+			6144: 2105352,
+			6400: 0,
+			6656: 8,
+			6912: 270532608,
+			7168: 8192,
+			7424: 268443656,
+			7680: 270540800,
+			7936: 2097160,
+			4224: 8,
+			4480: 2105344,
+			4736: 2097152,
+			4992: 268435464,
+			5248: 268443648,
+			5504: 8200,
+			5760: 270540808,
+			6016: 270532608,
+			6272: 270540800,
+			6528: 270532616,
+			6784: 8192,
+			7040: 2105352,
+			7296: 2097160,
+			7552: 0,
+			7808: 268435456,
+			8064: 268443656
+		}, {
+			"0": 1048576,
+			16: 33555457,
+			32: 1024,
+			48: 1049601,
+			64: 34604033,
+			80: 0,
+			96: 1,
+			112: 34603009,
+			128: 33555456,
+			144: 1048577,
+			160: 33554433,
+			176: 34604032,
+			192: 34603008,
+			208: 1025,
+			224: 1049600,
+			240: 33554432,
+			8: 34603009,
+			24: 0,
+			40: 33555457,
+			56: 34604032,
+			72: 1048576,
+			88: 33554433,
+			104: 33554432,
+			120: 1025,
+			136: 1049601,
+			152: 33555456,
+			168: 34603008,
+			184: 1048577,
+			200: 1024,
+			216: 34604033,
+			232: 1,
+			248: 1049600,
+			256: 33554432,
+			272: 1048576,
+			288: 33555457,
+			304: 34603009,
+			320: 1048577,
+			336: 33555456,
+			352: 34604032,
+			368: 1049601,
+			384: 1025,
+			400: 34604033,
+			416: 1049600,
+			432: 1,
+			448: 0,
+			464: 34603008,
+			480: 33554433,
+			496: 1024,
+			264: 1049600,
+			280: 33555457,
+			296: 34603009,
+			312: 1,
+			328: 33554432,
+			344: 1048576,
+			360: 1025,
+			376: 34604032,
+			392: 33554433,
+			408: 34603008,
+			424: 0,
+			440: 34604033,
+			456: 1049601,
+			472: 1024,
+			488: 33555456,
+			504: 1048577
+		}, {
+			"0": 134219808,
+			1: 131072,
+			2: 134217728,
+			3: 32,
+			4: 131104,
+			5: 134350880,
+			6: 134350848,
+			7: 2048,
+			8: 134348800,
+			9: 134219776,
+			10: 133120,
+			11: 134348832,
+			12: 2080,
+			13: 0,
+			14: 134217760,
+			15: 133152,
+			2147483648: 2048,
+			2147483649: 134350880,
+			2147483650: 134219808,
+			2147483651: 134217728,
+			2147483652: 134348800,
+			2147483653: 133120,
+			2147483654: 133152,
+			2147483655: 32,
+			2147483656: 134217760,
+			2147483657: 2080,
+			2147483658: 131104,
+			2147483659: 134350848,
+			2147483660: 0,
+			2147483661: 134348832,
+			2147483662: 134219776,
+			2147483663: 131072,
+			16: 133152,
+			17: 134350848,
+			18: 32,
+			19: 2048,
+			20: 134219776,
+			21: 134217760,
+			22: 134348832,
+			23: 131072,
+			24: 0,
+			25: 131104,
+			26: 134348800,
+			27: 134219808,
+			28: 134350880,
+			29: 133120,
+			30: 2080,
+			31: 134217728,
+			2147483664: 131072,
+			2147483665: 2048,
+			2147483666: 134348832,
+			2147483667: 133152,
+			2147483668: 32,
+			2147483669: 134348800,
+			2147483670: 134217728,
+			2147483671: 134219808,
+			2147483672: 134350880,
+			2147483673: 134217760,
+			2147483674: 134219776,
+			2147483675: 0,
+			2147483676: 133120,
+			2147483677: 2080,
+			2147483678: 131104,
+			2147483679: 134350848
+		}],
+		x = [4160749569, 528482304, 33030144, 2064384, 129024, 8064, 504, 2147483679],
+		r = s.DES = n.extend({
+			_doReset: function() {
+				for (var b = this._key.words, a = [], c = 0; 56 > c; c++) {
+					var d = q[c] - 1;
+					a[c] = b[d >>> 5] >>> 31 - d % 32 & 1
+				}
+				b = this._subKeys = [];
+				for (d = 0; 16 > d; d++) {
+					for (var f = b[d] = [], l = v[d], c = 0; 24 > c; c++) f[c / 6 | 0] |= a[(w[c] - 1 + l) % 28] << 31 - c %
+						6, f[4 + (c / 6 | 0)] |= a[28 + (w[c + 24] - 1 + l) % 28] << 31 - c % 6;
+					f[0] = f[0] << 1 | f[0] >>> 31;
+					for (c = 1; 7 > c; c++) f[c] >>>=
+						4 * (c - 1) + 3;
+					f[7] = f[7] << 5 | f[7] >>> 27
+				}
+				a = this._invSubKeys = [];
+				for (c = 0; 16 > c; c++) a[c] = b[15 - c]
+			},
+			encryptBlock: function(b, a) {
+				this._doCryptBlock(b, a, this._subKeys)
+			},
+			decryptBlock: function(b, a) {
+				this._doCryptBlock(b, a, this._invSubKeys)
+			},
+			_doCryptBlock: function(e, a, c) {
+				this._lBlock = e[a];
+				this._rBlock = e[a + 1];
+				u.call(this, 4, 252645135);
+				u.call(this, 16, 65535);
+				l.call(this, 2, 858993459);
+				l.call(this, 8, 16711935);
+				u.call(this, 1, 1431655765);
+				for (var d = 0; 16 > d; d++) {
+					for (var f = c[d], n = this._lBlock, p = this._rBlock, q = 0, r = 0; 8 > r; r++) q |= b[r][((p ^
+						f[r]) & x[r]) >>> 0];
+					this._lBlock = p;
+					this._rBlock = n ^ q
+				}
+				c = this._lBlock;
+				this._lBlock = this._rBlock;
+				this._rBlock = c;
+				u.call(this, 1, 1431655765);
+				l.call(this, 8, 16711935);
+				l.call(this, 2, 858993459);
+				u.call(this, 16, 65535);
+				u.call(this, 4, 252645135);
+				e[a] = this._lBlock;
+				e[a + 1] = this._rBlock
+			},
+			keySize: 2,
+			ivSize: 2,
+			blockSize: 2
+		});
+	d.DES = n._createHelper(r);
+	s = s.TripleDES = n.extend({
+		_doReset: function() {
+			var b = this._key.words;
+			this._des1 = r.createEncryptor(p.create(b.slice(0, 2)));
+			this._des2 = r.createEncryptor(p.create(b.slice(2, 4)));
+			this._des3 =
+				r.createEncryptor(p.create(b.slice(4, 6)))
+		},
+		encryptBlock: function(b, a) {
+			this._des1.encryptBlock(b, a);
+			this._des2.decryptBlock(b, a);
+			this._des3.encryptBlock(b, a)
+		},
+		decryptBlock: function(b, a) {
+			this._des3.decryptBlock(b, a);
+			this._des2.encryptBlock(b, a);
+			this._des1.decryptBlock(b, a)
+		},
+		keySize: 6,
+		ivSize: 2,
+		blockSize: 2
+	});
+	d.TripleDES = n._createHelper(s)
+})();
+CryptoJS.mode.ECB = (function() {
+	var ECB = CryptoJS.lib.BlockCipherMode.extend();
+
+	ECB.Encryptor = ECB.extend({
+		processBlock: function(words, offset) {
+			this._cipher.encryptBlock(words, offset);
+		}
+	});
+
+	ECB.Decryptor = ECB.extend({
+		processBlock: function(words, offset) {
+			this._cipher.decryptBlock(words, offset);
+		}
+	});
+
+	return ECB;
+}());

+ 1 - 0
utils/index.ts

@@ -0,0 +1 @@
+export * as common from './common';

+ 56 - 0
utils/sseParse.ts

@@ -0,0 +1,56 @@
+export function createSSEParser(callback) {
+	let buffer = '';
+	let currentEvent = 'message'; // 默认事件名
+
+	// 处理接收到的数据
+	function parse(chunk) {
+		// 将新数据添加到缓冲区
+		buffer += chunk;
+
+		// 尝试分割完整的消息(以\n\n为分隔符)
+		const messages = buffer.split('\n\n');
+
+		// 如果最后一段不完整,保留在缓冲区
+		buffer = messages.pop();
+
+		// 处理每一条完整的消息
+		messages.forEach((message) => {
+			if (message.trim()) {
+				// 默认事件message
+				currentEvent = 'message';
+				parseMessage(message);
+			}
+		});
+	}
+
+	// 解析单条消息
+	function parseMessage(message) {
+		const lines = message.split('\n');
+		let event = 'message';
+		const dataLines = [];
+
+		// 解析每一行
+		lines.forEach((line) => {
+			if (line.startsWith('event:')) {
+				event = line.slice(6).trim();
+				currentEvent = event;
+			} else if (line.startsWith('data:')) {
+				dataLines.push(line.slice(5).trim());
+			}
+		});
+
+		// 如果有数据行,处理每一行数据
+		if (dataLines.length > 0) {
+			dataLines.forEach((data) => {
+				callback(currentEvent, data);
+			});
+		} else if (currentEvent !== 'message') {
+			// 如果只有事件没有数据,也触发回调
+			callback(currentEvent, null);
+		}
+	}
+
+	return {
+		parse,
+	};
+}