小程序初始提交
This commit is contained in:
28
cool-unix/cool/hooks/cache.ts
Normal file
28
cool-unix/cool/hooks/cache.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { reactive, watch } from "vue";
|
||||
import { isDark } from "../theme";
|
||||
|
||||
type CacheData = {
|
||||
key: number;
|
||||
};
|
||||
|
||||
type UseCache = {
|
||||
cache: CacheData;
|
||||
};
|
||||
|
||||
export const useCache = (source: () => any[]): UseCache => {
|
||||
const cache = reactive<CacheData>({
|
||||
key: 0
|
||||
});
|
||||
|
||||
watch(source, () => {
|
||||
cache.key++;
|
||||
});
|
||||
|
||||
watch(isDark, () => {
|
||||
cache.key++;
|
||||
});
|
||||
|
||||
return {
|
||||
cache
|
||||
};
|
||||
};
|
||||
6
cool-unix/cool/hooks/index.ts
Normal file
6
cool-unix/cool/hooks/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from "./cache";
|
||||
export * from "./long-press";
|
||||
export * from "./pager";
|
||||
export * from "./parent";
|
||||
export * from "./refs";
|
||||
export * from "./wx";
|
||||
100
cool-unix/cool/hooks/long-press.ts
Normal file
100
cool-unix/cool/hooks/long-press.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { vibrate } from "@/uni_modules/cool-vibrate";
|
||||
import { onUnmounted, ref, type Ref } from "vue";
|
||||
|
||||
// 长按触发延迟时间,单位毫秒
|
||||
const DELAY = 500;
|
||||
// 长按重复执行间隔时间,单位毫秒
|
||||
const REPEAT = 100;
|
||||
|
||||
/**
|
||||
* 长按操作钩子函数返回类型
|
||||
*/
|
||||
type UseLongPress = {
|
||||
// 开始长按
|
||||
start: (cb: () => void) => void;
|
||||
// 停止长按
|
||||
stop: () => void;
|
||||
// 清除定时器
|
||||
clear: () => void;
|
||||
// 是否正在长按中
|
||||
isPressing: Ref<boolean>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 长按操作钩子函数
|
||||
* 支持长按持续触发,可用于数字输入框等需要连续操作的场景
|
||||
*/
|
||||
export const useLongPress = (): UseLongPress => {
|
||||
// 是否正在长按中
|
||||
const isPressing = ref(false);
|
||||
// 长按延迟定时器
|
||||
let pressTimer: number = 0;
|
||||
// 重复执行定时器
|
||||
let repeatTimer: number = 0;
|
||||
|
||||
/**
|
||||
* 清除所有定时器
|
||||
* 重置长按状态
|
||||
*/
|
||||
const clear = () => {
|
||||
// 清除长按延迟定时器
|
||||
if (pressTimer != 0) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = 0;
|
||||
}
|
||||
// 清除重复执行定时器
|
||||
if (repeatTimer != 0) {
|
||||
clearInterval(repeatTimer);
|
||||
repeatTimer = 0;
|
||||
}
|
||||
// 重置长按状态
|
||||
isPressing.value = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 开始长按操作
|
||||
* @param cb 长按时重复执行的回调函数
|
||||
*/
|
||||
const start = (cb: () => void) => {
|
||||
// 清除已有定时器
|
||||
clear();
|
||||
|
||||
// 立即执行一次回调
|
||||
cb();
|
||||
|
||||
// 延迟500ms后开始长按
|
||||
// @ts-ignore
|
||||
pressTimer = setTimeout(() => {
|
||||
// 震动
|
||||
vibrate(1);
|
||||
|
||||
// 设置长按状态
|
||||
isPressing.value = true;
|
||||
// 每100ms重复执行回调
|
||||
// @ts-ignore
|
||||
repeatTimer = setInterval(() => {
|
||||
cb();
|
||||
}, REPEAT);
|
||||
}, DELAY);
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止长按操作
|
||||
* 清除定时器并重置状态
|
||||
*/
|
||||
const stop = () => {
|
||||
clear();
|
||||
};
|
||||
|
||||
// 组件卸载时清理定时器
|
||||
onUnmounted(() => {
|
||||
clear();
|
||||
});
|
||||
|
||||
return {
|
||||
start,
|
||||
stop,
|
||||
clear,
|
||||
isPressing
|
||||
};
|
||||
};
|
||||
113
cool-unix/cool/hooks/pager.ts
Normal file
113
cool-unix/cool/hooks/pager.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { assign, parse } from "../utils";
|
||||
import { useListView, type ClListViewItem } from "@/uni_modules/cool-ui";
|
||||
|
||||
// 分页参数类型
|
||||
type Pagination = {
|
||||
page: number; // 当前页码
|
||||
size: number; // 每页数量
|
||||
total: number; // 总数量
|
||||
};
|
||||
|
||||
// 分页响应数据类型
|
||||
type PagerResponse = {
|
||||
list: UTSJSONObject[]; // 列表数据
|
||||
pagination: Pagination; // 分页信息
|
||||
};
|
||||
|
||||
// 分页回调函数类型
|
||||
type PagerCallback = (params: UTSJSONObject, ctx: Pager) => void | Promise<void>;
|
||||
|
||||
// 分页器类
|
||||
export class Pager {
|
||||
public page = 1; // 当前页码
|
||||
public size = 20; // 每页数量
|
||||
public total = 0; // 总数量
|
||||
public list = ref<UTSJSONObject[]>([]); // 列表数据
|
||||
public loading = ref(false); // 加载状态
|
||||
public refreshing = ref(false); // 刷新状态
|
||||
public finished = ref(false); // 是否加载完成
|
||||
public params = {} as UTSJSONObject; // 请求参数
|
||||
public cb: PagerCallback | null = null; // 回调函数
|
||||
|
||||
// 构造函数
|
||||
constructor(cb: PagerCallback) {
|
||||
this.cb = cb;
|
||||
}
|
||||
|
||||
// 完成加载
|
||||
done() {
|
||||
this.loading.value = false;
|
||||
}
|
||||
|
||||
// 清空数据
|
||||
clear() {
|
||||
this.list.value = [];
|
||||
this.finished.value = false;
|
||||
this.refreshing.value = false;
|
||||
this.loading.value = false;
|
||||
}
|
||||
|
||||
// 渲染数据
|
||||
public render = (res: any) => {
|
||||
const { list, pagination } = parse<PagerResponse>(res)!;
|
||||
|
||||
// 更新分页信息
|
||||
this.page = pagination.page;
|
||||
this.size = pagination.size;
|
||||
this.total = pagination.total;
|
||||
|
||||
// 更新列表数据
|
||||
if (this.params.page == 1) {
|
||||
this.list.value = [...list];
|
||||
} else {
|
||||
this.list.value.push(...list);
|
||||
}
|
||||
|
||||
// 更新加载完成状态
|
||||
this.finished.value = this.list.value.length >= this.total;
|
||||
|
||||
// 完成加载
|
||||
this.done();
|
||||
};
|
||||
|
||||
// 刷新数据
|
||||
public refresh = async (params: UTSJSONObject) => {
|
||||
// 合并参数
|
||||
this.params = assign(this.params, params);
|
||||
|
||||
// 构建请求参数
|
||||
const data = {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
...this.params
|
||||
};
|
||||
|
||||
// 开始加载
|
||||
this.loading.value = true;
|
||||
|
||||
// 发起请求
|
||||
await this.cb!(data, this);
|
||||
};
|
||||
|
||||
// 加载更多数据
|
||||
public loadMore = () => {
|
||||
if (this.loading.value || this.finished.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refresh({
|
||||
page: this.page + 1
|
||||
});
|
||||
};
|
||||
|
||||
// 列表视图数据
|
||||
public listView = computed<ClListViewItem[]>(() => {
|
||||
return useListView(this.list.value);
|
||||
});
|
||||
}
|
||||
|
||||
// 创建分页器实例
|
||||
export const usePager = (cb: PagerCallback): Pager => {
|
||||
return new Pager(cb);
|
||||
};
|
||||
22
cool-unix/cool/hooks/parent.ts
Normal file
22
cool-unix/cool/hooks/parent.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { getCurrentInstance } from "vue";
|
||||
|
||||
/**
|
||||
* 获取父组件
|
||||
* @param name 组件名称
|
||||
* @example useParent<ClFormComponentPublicInstance>("cl-form")
|
||||
* @returns 父组件
|
||||
*/
|
||||
export function useParent<T>(name: string): T | null {
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
|
||||
let p = proxy?.$parent;
|
||||
|
||||
while (p != null) {
|
||||
if (p.$options.name == name) {
|
||||
return p as T | null;
|
||||
}
|
||||
p = p.$parent;
|
||||
}
|
||||
|
||||
return p as T | null;
|
||||
}
|
||||
122
cool-unix/cool/hooks/refs.ts
Normal file
122
cool-unix/cool/hooks/refs.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { reactive } from "vue";
|
||||
import { isNull } from "../utils";
|
||||
|
||||
// #ifdef APP
|
||||
// @ts-ignore
|
||||
type Instance = ComponentPublicInstance | null;
|
||||
// #endif
|
||||
|
||||
// #ifndef APP
|
||||
// @ts-ignore
|
||||
type Instance = any;
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* Refs 类用于管理组件引用,便于在组合式 API 中获取、操作子组件实例。
|
||||
*/
|
||||
class Refs {
|
||||
// 存储所有 ref 的响应式对象,key 为 ref 名称,value 为组件实例
|
||||
data = reactive({} as UTSJSONObject);
|
||||
|
||||
/**
|
||||
* 生成 ref 绑定函数,用于在模板中设置 ref。
|
||||
* @param name ref 名称
|
||||
* @returns 绑定函数 (el: Instance) => void
|
||||
*/
|
||||
set(name: string) {
|
||||
return (el: Instance) => {
|
||||
this.data[name] = el;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的组件实例
|
||||
* @param name ref 名称
|
||||
* @returns 组件实例或 null
|
||||
*/
|
||||
get(name: string): Instance {
|
||||
const d = this.data[name] as ComponentPublicInstance;
|
||||
|
||||
if (isNull(d)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件实例暴露的属性或方法(兼容不同平台)
|
||||
* @param name ref 名称
|
||||
* @param key 暴露的属性名
|
||||
* @returns 属性值或 null
|
||||
*/
|
||||
getExposed<T>(name: string, key: string): T | null {
|
||||
// #ifdef APP-ANDROID
|
||||
const d = this.get(name);
|
||||
|
||||
if (isNull(d)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 安卓平台下,$exposed 为 Map<string, any>
|
||||
const ex = d!.$exposed as Map<string, any>;
|
||||
|
||||
if (isNull(ex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ex[key] as T | null;
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-ANDROID
|
||||
// 其他平台直接通过属性访问
|
||||
return this.get(name)?.[key] as T;
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用组件实例暴露的方法,并返回结果
|
||||
* @param name ref 名称
|
||||
* @param method 方法名
|
||||
* @param data 传递的数据
|
||||
* @returns 方法返回值
|
||||
*/
|
||||
call<T>(name: string, method: string, data: UTSJSONObject | null = null): T {
|
||||
return this.get(name)!.$callMethod(method, data) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用组件实例暴露的方法,无返回值
|
||||
* @param name ref 名称
|
||||
* @param method 方法名
|
||||
* @param data 传递的数据
|
||||
*/
|
||||
callMethod(name: string, method: string, data: UTSJSONObject | null = null): void {
|
||||
this.get(name)!.$callMethod(method, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用组件的 open 方法,常用于弹窗、抽屉等组件
|
||||
* @param name ref 名称
|
||||
* @param data 传递的数据
|
||||
*/
|
||||
open(name: string, data: UTSJSONObject | null = null) {
|
||||
this.callMethod(name, "open", data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用组件的 close 方法,常用于弹窗、抽屉等组件
|
||||
* @param name ref 名称
|
||||
*/
|
||||
close(name: string) {
|
||||
return this.callMethod(name, "close");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* useRefs 组合式函数,返回 Refs 实例
|
||||
* @returns Refs 实例
|
||||
*/
|
||||
export function useRefs(): Refs {
|
||||
return new Refs();
|
||||
}
|
||||
247
cool-unix/cool/hooks/wx.ts
Normal file
247
cool-unix/cool/hooks/wx.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import { ref } from "vue";
|
||||
import { assign, getUrlParam, storage } from "../utils";
|
||||
import { request } from "../service";
|
||||
import { t } from "@/locale";
|
||||
import { config } from "@/config";
|
||||
|
||||
// #ifdef H5
|
||||
import wx from "weixin-js-sdk";
|
||||
// #endif
|
||||
|
||||
// 微信配置类型
|
||||
type WxConfig = {
|
||||
appId: string;
|
||||
};
|
||||
|
||||
// 微信相关功能封装类
|
||||
export class Wx {
|
||||
// 微信登录code
|
||||
code = ref("");
|
||||
|
||||
/**
|
||||
* 获取微信登录code
|
||||
*/
|
||||
async getCode(): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.login({
|
||||
provider: "weixin",
|
||||
success: (res) => {
|
||||
this.code.value = res.code;
|
||||
resolve(res.code);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
resolve("");
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
// 公众号配置
|
||||
mpConfig: WxConfig = {
|
||||
appId: ""
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断当前是否为微信浏览器
|
||||
*/
|
||||
isWxBrowser() {
|
||||
const ua: string = window.navigator.userAgent.toLowerCase();
|
||||
if (ua.match(/MicroMessenger/i) != null) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公众号配置信息,并初始化微信JS-SDK
|
||||
*/
|
||||
getMpConfig() {
|
||||
if (this.isWxBrowser()) {
|
||||
request({
|
||||
url: "/app/user/common/wxMpConfig",
|
||||
method: "POST",
|
||||
data: {
|
||||
url: `${location.origin}${location.pathname}`
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res != null) {
|
||||
wx.config({
|
||||
debug: config.wx.debug,
|
||||
jsApiList: res.jsApiList || ["chooseWXPay"],
|
||||
appId: res.appId,
|
||||
timestamp: res.timestamp,
|
||||
nonceStr: res.nonceStr,
|
||||
signature: res.signature,
|
||||
openTagList: res.openTagList
|
||||
});
|
||||
|
||||
// 合并配置到mpConfig
|
||||
assign(this.mpConfig, res);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到微信授权页面
|
||||
*/
|
||||
mpAuth() {
|
||||
const { appId } = this.mpConfig;
|
||||
|
||||
const redirect_uri = encodeURIComponent(
|
||||
`${location.origin}${location.pathname}#/pages/user/login`
|
||||
);
|
||||
const response_type = "code";
|
||||
const scope = "snsapi_userinfo";
|
||||
const state = "STATE";
|
||||
|
||||
const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}#wechat_redirect`;
|
||||
|
||||
location.href = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号登录,获取code
|
||||
*/
|
||||
mpLogin() {
|
||||
return new Promise((resolve) => {
|
||||
const code = getUrlParam("code");
|
||||
const mpCode = storage.get("mpCode");
|
||||
|
||||
// 去除url中的code参数,避免重复
|
||||
const url = window.location.href.replace(/(\?[^#]*)#/, "#");
|
||||
window.history.replaceState({}, "", url);
|
||||
|
||||
if (code != mpCode) {
|
||||
storage.set("mpCode", code, 1000 * 60 * 5);
|
||||
resolve(code);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号微信支付
|
||||
* @param params 支付参数
|
||||
*/
|
||||
mpPay(params: wx.IchooseWXPay & { timeStamp: number }): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isWxBrowser()) {
|
||||
return reject({ message: t("请在微信浏览器中打开") });
|
||||
}
|
||||
|
||||
wx.chooseWXPay({
|
||||
...params,
|
||||
timestamp: params.timeStamp,
|
||||
success() {
|
||||
resolve();
|
||||
},
|
||||
complete(e: { errMsg: string }) {
|
||||
switch (e.errMsg) {
|
||||
case "chooseWXPay:cancel":
|
||||
reject({ message: t("已取消支付") });
|
||||
break;
|
||||
|
||||
default:
|
||||
reject({ message: t("支付失败") });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
/**
|
||||
* 小程序登录,获取用户信息和code
|
||||
*/
|
||||
miniLogin(): Promise<{
|
||||
code: string;
|
||||
iv: string;
|
||||
encryptedData: string;
|
||||
signature: string;
|
||||
rawData: string;
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 兼容 Mac,Mac 端需用 getUserInfo
|
||||
const k = uni.getDeviceInfo().platform === "mac" ? "getUserInfo" : "getUserProfile";
|
||||
|
||||
uni[k]({
|
||||
lang: "zh_CN",
|
||||
desc: t("授权信息仅用于用户登录"),
|
||||
success: ({ iv, encryptedData, signature, rawData }) => {
|
||||
const next = () => {
|
||||
resolve({
|
||||
iv,
|
||||
encryptedData,
|
||||
signature,
|
||||
rawData,
|
||||
code: this.code.value
|
||||
});
|
||||
};
|
||||
|
||||
// 检查登录状态是否过期
|
||||
uni.checkSession({
|
||||
success: () => {
|
||||
next();
|
||||
},
|
||||
fail: () => {
|
||||
this.getCode().then(() => {
|
||||
next();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(`[useWx.miniLogin] error`, err);
|
||||
this.getCode();
|
||||
|
||||
reject(t("登录授权失败"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序微信支付
|
||||
* @param params 支付参数
|
||||
*/
|
||||
miniPay(params: any): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
provider: "wxpay",
|
||||
...params,
|
||||
success() {
|
||||
resolve();
|
||||
},
|
||||
fail() {
|
||||
reject(t("已取消支付"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* useWx 钩子函数,后续可扩展
|
||||
*/
|
||||
export const useWx = (): Wx => {
|
||||
const wx = new Wx();
|
||||
|
||||
onReady(() => {
|
||||
wx.getCode();
|
||||
|
||||
// #ifdef H5
|
||||
wx.getMpConfig();
|
||||
// #endif
|
||||
});
|
||||
|
||||
return wx;
|
||||
};
|
||||
Reference in New Issue
Block a user