@yh-ui/hooks 0.1.17 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useAiPersistence = useAiPersistence;
7
+ var _vue = require("vue");
8
+ var _storage = require("../storage/index.cjs");
9
+ function useAiPersistence(options = {}) {
10
+ const {
11
+ storage = new _storage.IndexedDBAdapter(),
12
+ conversationKey = "ai-conversations",
13
+ autoSave = true
14
+ } = options;
15
+ const conversations = (0, _vue.ref)([]);
16
+ const currentConversationId = (0, _vue.ref)(null);
17
+ const isLoading = (0, _vue.ref)(false);
18
+ const isSaving = (0, _vue.ref)(false);
19
+ const error = (0, _vue.ref)(null);
20
+ const loadConversations = async () => {
21
+ isLoading.value = true;
22
+ error.value = null;
23
+ try {
24
+ const data = await storage.get(conversationKey);
25
+ conversations.value = data || [];
26
+ if (conversations.value.length > 0) {
27
+ const lastConversation = conversations.value[conversations.value.length - 1];
28
+ currentConversationId.value = lastConversation.id;
29
+ }
30
+ } catch (e) {
31
+ error.value = e instanceof Error ? e : new Error("Failed to load conversations");
32
+ } finally {
33
+ isLoading.value = false;
34
+ }
35
+ };
36
+ const saveConversations = async () => {
37
+ isSaving.value = true;
38
+ error.value = null;
39
+ try {
40
+ await storage.set(conversationKey, conversations.value);
41
+ } catch (e) {
42
+ error.value = e instanceof Error ? e : new Error("Failed to save conversations");
43
+ } finally {
44
+ isSaving.value = false;
45
+ }
46
+ };
47
+ const createConversation = (title = "New Conversation") => {
48
+ const now = Date.now();
49
+ const conversation = {
50
+ id: `conv-${now}`,
51
+ title,
52
+ messages: [],
53
+ createdAt: now,
54
+ updatedAt: now
55
+ };
56
+ conversations.value.push(conversation);
57
+ currentConversationId.value = conversation.id;
58
+ if (autoSave) {
59
+ saveConversations();
60
+ }
61
+ return conversation;
62
+ };
63
+ const deleteConversation = id => {
64
+ const index = conversations.value.findIndex(c => c.id === id);
65
+ if (index !== -1) {
66
+ conversations.value.splice(index, 1);
67
+ if (currentConversationId.value === id) {
68
+ currentConversationId.value = conversations.value.length > 0 ? conversations.value[conversations.value.length - 1].id : null;
69
+ }
70
+ if (autoSave) {
71
+ saveConversations();
72
+ }
73
+ }
74
+ };
75
+ const getCurrentConversation = () => {
76
+ return conversations.value.find(c => c.id === currentConversationId.value);
77
+ };
78
+ const addMessage = message => {
79
+ const conversation = getCurrentConversation();
80
+ if (!conversation) return;
81
+ const newMessage = {
82
+ ...message,
83
+ id: `msg-${Date.now()}`,
84
+ timestamp: Date.now()
85
+ };
86
+ conversation.messages.push(newMessage);
87
+ conversation.updatedAt = Date.now();
88
+ if (autoSave) {
89
+ saveConversations();
90
+ }
91
+ return newMessage;
92
+ };
93
+ const updateMessage = (messageId, updates) => {
94
+ const conversation = getCurrentConversation();
95
+ if (!conversation) return;
96
+ const messageIndex = conversation.messages.findIndex(m => m.id === messageId);
97
+ if (messageIndex !== -1) {
98
+ conversation.messages[messageIndex] = {
99
+ ...conversation.messages[messageIndex],
100
+ ...updates
101
+ };
102
+ conversation.updatedAt = Date.now();
103
+ if (autoSave) {
104
+ saveConversations();
105
+ }
106
+ }
107
+ };
108
+ const clearCurrentConversation = () => {
109
+ const conversation = getCurrentConversation();
110
+ if (!conversation) return;
111
+ conversation.messages = [];
112
+ conversation.updatedAt = Date.now();
113
+ if (autoSave) {
114
+ saveConversations();
115
+ }
116
+ };
117
+ const exportConversations = () => {
118
+ return JSON.stringify(conversations.value, null, 2);
119
+ };
120
+ const importConversations = async json => {
121
+ try {
122
+ const imported = JSON.parse(json);
123
+ if (!Array.isArray(imported)) {
124
+ throw new Error("Invalid format: expected array");
125
+ }
126
+ conversations.value = imported;
127
+ await saveConversations();
128
+ return true;
129
+ } catch (e) {
130
+ error.value = e instanceof Error ? e : new Error("Failed to import conversations");
131
+ return false;
132
+ }
133
+ };
134
+ const setCurrentConversation = id => {
135
+ if (conversations.value.some(c => c.id === id)) {
136
+ currentConversationId.value = id;
137
+ }
138
+ };
139
+ (0, _vue.onMounted)(() => {
140
+ loadConversations();
141
+ });
142
+ return {
143
+ conversations,
144
+ currentConversationId,
145
+ isLoading,
146
+ isSaving,
147
+ error,
148
+ loadConversations,
149
+ saveConversations,
150
+ createConversation,
151
+ deleteConversation,
152
+ getCurrentConversation,
153
+ addMessage,
154
+ updateMessage,
155
+ clearCurrentConversation,
156
+ exportConversations,
157
+ importConversations,
158
+ setCurrentConversation
159
+ };
160
+ }
@@ -0,0 +1,62 @@
1
+ import { type StorageAdapter } from '../storage';
2
+ export interface ConversationMessage {
3
+ id: string;
4
+ role: 'user' | 'assistant' | 'system';
5
+ content: string;
6
+ timestamp: number;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ export interface Conversation {
10
+ id: string;
11
+ title: string;
12
+ messages: ConversationMessage[];
13
+ createdAt: number;
14
+ updatedAt: number;
15
+ }
16
+ export interface UseAiPersistenceOptions {
17
+ storage?: StorageAdapter;
18
+ conversationKey?: string;
19
+ autoSave?: boolean;
20
+ }
21
+ export declare function useAiPersistence(options?: UseAiPersistenceOptions): {
22
+ conversations: import("vue").Ref<{
23
+ id: string;
24
+ title: string;
25
+ messages: {
26
+ id: string;
27
+ role: "user" | "assistant" | "system";
28
+ content: string;
29
+ timestamp: number;
30
+ metadata?: Record<string, unknown> | undefined;
31
+ }[];
32
+ createdAt: number;
33
+ updatedAt: number;
34
+ }[], Conversation[] | {
35
+ id: string;
36
+ title: string;
37
+ messages: {
38
+ id: string;
39
+ role: "user" | "assistant" | "system";
40
+ content: string;
41
+ timestamp: number;
42
+ metadata?: Record<string, unknown> | undefined;
43
+ }[];
44
+ createdAt: number;
45
+ updatedAt: number;
46
+ }[]>;
47
+ currentConversationId: import("vue").Ref<string | null, string | null>;
48
+ isLoading: import("vue").Ref<boolean, boolean>;
49
+ isSaving: import("vue").Ref<boolean, boolean>;
50
+ error: import("vue").Ref<Error | null, Error | null>;
51
+ loadConversations: () => Promise<void>;
52
+ saveConversations: () => Promise<void>;
53
+ createConversation: (title?: string) => Conversation;
54
+ deleteConversation: (id: string) => void;
55
+ getCurrentConversation: () => Conversation | undefined;
56
+ addMessage: (message: Omit<ConversationMessage, "id" | "timestamp">) => ConversationMessage | undefined;
57
+ updateMessage: (messageId: string, updates: Partial<ConversationMessage>) => void;
58
+ clearCurrentConversation: () => void;
59
+ exportConversations: () => string;
60
+ importConversations: (json: string) => Promise<boolean>;
61
+ setCurrentConversation: (id: string) => void;
62
+ };
@@ -0,0 +1,154 @@
1
+ import { ref, onMounted } from "vue";
2
+ import { IndexedDBAdapter } from "../storage/index.mjs";
3
+ export function useAiPersistence(options = {}) {
4
+ const {
5
+ storage = new IndexedDBAdapter(),
6
+ conversationKey = "ai-conversations",
7
+ autoSave = true
8
+ } = options;
9
+ const conversations = ref([]);
10
+ const currentConversationId = ref(null);
11
+ const isLoading = ref(false);
12
+ const isSaving = ref(false);
13
+ const error = ref(null);
14
+ const loadConversations = async () => {
15
+ isLoading.value = true;
16
+ error.value = null;
17
+ try {
18
+ const data = await storage.get(conversationKey);
19
+ conversations.value = data || [];
20
+ if (conversations.value.length > 0) {
21
+ const lastConversation = conversations.value[conversations.value.length - 1];
22
+ currentConversationId.value = lastConversation.id;
23
+ }
24
+ } catch (e) {
25
+ error.value = e instanceof Error ? e : new Error("Failed to load conversations");
26
+ } finally {
27
+ isLoading.value = false;
28
+ }
29
+ };
30
+ const saveConversations = async () => {
31
+ isSaving.value = true;
32
+ error.value = null;
33
+ try {
34
+ await storage.set(conversationKey, conversations.value);
35
+ } catch (e) {
36
+ error.value = e instanceof Error ? e : new Error("Failed to save conversations");
37
+ } finally {
38
+ isSaving.value = false;
39
+ }
40
+ };
41
+ const createConversation = (title = "New Conversation") => {
42
+ const now = Date.now();
43
+ const conversation = {
44
+ id: `conv-${now}`,
45
+ title,
46
+ messages: [],
47
+ createdAt: now,
48
+ updatedAt: now
49
+ };
50
+ conversations.value.push(conversation);
51
+ currentConversationId.value = conversation.id;
52
+ if (autoSave) {
53
+ saveConversations();
54
+ }
55
+ return conversation;
56
+ };
57
+ const deleteConversation = (id) => {
58
+ const index = conversations.value.findIndex((c) => c.id === id);
59
+ if (index !== -1) {
60
+ conversations.value.splice(index, 1);
61
+ if (currentConversationId.value === id) {
62
+ currentConversationId.value = conversations.value.length > 0 ? conversations.value[conversations.value.length - 1].id : null;
63
+ }
64
+ if (autoSave) {
65
+ saveConversations();
66
+ }
67
+ }
68
+ };
69
+ const getCurrentConversation = () => {
70
+ return conversations.value.find((c) => c.id === currentConversationId.value);
71
+ };
72
+ const addMessage = (message) => {
73
+ const conversation = getCurrentConversation();
74
+ if (!conversation) return;
75
+ const newMessage = {
76
+ ...message,
77
+ id: `msg-${Date.now()}`,
78
+ timestamp: Date.now()
79
+ };
80
+ conversation.messages.push(newMessage);
81
+ conversation.updatedAt = Date.now();
82
+ if (autoSave) {
83
+ saveConversations();
84
+ }
85
+ return newMessage;
86
+ };
87
+ const updateMessage = (messageId, updates) => {
88
+ const conversation = getCurrentConversation();
89
+ if (!conversation) return;
90
+ const messageIndex = conversation.messages.findIndex((m) => m.id === messageId);
91
+ if (messageIndex !== -1) {
92
+ conversation.messages[messageIndex] = {
93
+ ...conversation.messages[messageIndex],
94
+ ...updates
95
+ };
96
+ conversation.updatedAt = Date.now();
97
+ if (autoSave) {
98
+ saveConversations();
99
+ }
100
+ }
101
+ };
102
+ const clearCurrentConversation = () => {
103
+ const conversation = getCurrentConversation();
104
+ if (!conversation) return;
105
+ conversation.messages = [];
106
+ conversation.updatedAt = Date.now();
107
+ if (autoSave) {
108
+ saveConversations();
109
+ }
110
+ };
111
+ const exportConversations = () => {
112
+ return JSON.stringify(conversations.value, null, 2);
113
+ };
114
+ const importConversations = async (json) => {
115
+ try {
116
+ const imported = JSON.parse(json);
117
+ if (!Array.isArray(imported)) {
118
+ throw new Error("Invalid format: expected array");
119
+ }
120
+ conversations.value = imported;
121
+ await saveConversations();
122
+ return true;
123
+ } catch (e) {
124
+ error.value = e instanceof Error ? e : new Error("Failed to import conversations");
125
+ return false;
126
+ }
127
+ };
128
+ const setCurrentConversation = (id) => {
129
+ if (conversations.value.some((c) => c.id === id)) {
130
+ currentConversationId.value = id;
131
+ }
132
+ };
133
+ onMounted(() => {
134
+ loadConversations();
135
+ });
136
+ return {
137
+ conversations,
138
+ currentConversationId,
139
+ isLoading,
140
+ isSaving,
141
+ error,
142
+ loadConversations,
143
+ saveConversations,
144
+ createConversation,
145
+ deleteConversation,
146
+ getCurrentConversation,
147
+ addMessage,
148
+ updateMessage,
149
+ clearCurrentConversation,
150
+ exportConversations,
151
+ importConversations,
152
+ setCurrentConversation
153
+ };
154
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useCountdown = useCountdown;
7
+ var _vue = require("vue");
8
+ function useCountdown(options) {
9
+ const {
10
+ time,
11
+ interval = 1e3,
12
+ onFinish,
13
+ onChange
14
+ } = options;
15
+ const remain = (0, _vue.ref)(time);
16
+ const timer = (0, _vue.ref)(null);
17
+ const isRunning = (0, _vue.computed)(() => timer.value !== null);
18
+ const stop = () => {
19
+ if (timer.value) {
20
+ clearInterval(timer.value);
21
+ timer.value = null;
22
+ }
23
+ };
24
+ const start = () => {
25
+ if (isRunning.value) return;
26
+ if (remain.value <= 0) return;
27
+ timer.value = setInterval(() => {
28
+ remain.value -= interval;
29
+ if (onChange) onChange(remain.value);
30
+ if (remain.value <= 0) {
31
+ remain.value = 0;
32
+ stop();
33
+ if (onFinish) onFinish();
34
+ }
35
+ }, interval);
36
+ };
37
+ const reset = newTime => {
38
+ stop();
39
+ remain.value = newTime !== void 0 ? newTime : time;
40
+ };
41
+ (0, _vue.onUnmounted)(() => {
42
+ stop();
43
+ });
44
+ return {
45
+ remain,
46
+ isRunning,
47
+ start,
48
+ stop,
49
+ reset
50
+ };
51
+ }
@@ -0,0 +1,17 @@
1
+ export interface CountdownOptions {
2
+ time: number;
3
+ interval?: number;
4
+ onFinish?: () => void;
5
+ onChange?: (current: number) => void;
6
+ }
7
+ /**
8
+ * 倒计时逻辑 Hook
9
+ * @param options
10
+ */
11
+ export declare function useCountdown(options: CountdownOptions): {
12
+ remain: import("vue").Ref<number, number>;
13
+ isRunning: import("vue").ComputedRef<boolean>;
14
+ start: () => void;
15
+ stop: () => void;
16
+ reset: (newTime?: number) => void;
17
+ };
@@ -0,0 +1,40 @@
1
+ import { ref, computed, onUnmounted } from "vue";
2
+ export function useCountdown(options) {
3
+ const { time, interval = 1e3, onFinish, onChange } = options;
4
+ const remain = ref(time);
5
+ const timer = ref(null);
6
+ const isRunning = computed(() => timer.value !== null);
7
+ const stop = () => {
8
+ if (timer.value) {
9
+ clearInterval(timer.value);
10
+ timer.value = null;
11
+ }
12
+ };
13
+ const start = () => {
14
+ if (isRunning.value) return;
15
+ if (remain.value <= 0) return;
16
+ timer.value = setInterval(() => {
17
+ remain.value -= interval;
18
+ if (onChange) onChange(remain.value);
19
+ if (remain.value <= 0) {
20
+ remain.value = 0;
21
+ stop();
22
+ if (onFinish) onFinish();
23
+ }
24
+ }, interval);
25
+ };
26
+ const reset = (newTime) => {
27
+ stop();
28
+ remain.value = newTime !== void 0 ? newTime : time;
29
+ };
30
+ onUnmounted(() => {
31
+ stop();
32
+ });
33
+ return {
34
+ remain,
35
+ isRunning,
36
+ start,
37
+ stop,
38
+ reset
39
+ };
40
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useSKU = useSKU;
7
+ var _vue = require("vue");
8
+ function useSKU(specs, skus, initialSelection = []) {
9
+ const selectedValueIds = (0, _vue.ref)(initialSelection);
10
+ const pathDict = (0, _vue.computed)(() => {
11
+ const dict = {};
12
+ skus.forEach(sku => {
13
+ if (sku.stock <= 0) return;
14
+ const powerSet = getPowerSet(sku.specValueIds);
15
+ powerSet.forEach(path => {
16
+ const key = path.join(",");
17
+ dict[key] = (dict[key] || 0) + sku.stock;
18
+ });
19
+ });
20
+ return dict;
21
+ });
22
+ const isValueSelectable = (specIndex, valueId) => {
23
+ const tempSelected = [...selectedValueIds.value];
24
+ if (tempSelected[specIndex] === valueId) {
25
+ tempSelected[specIndex] = "";
26
+ } else {
27
+ tempSelected[specIndex] = valueId;
28
+ }
29
+ const query = tempSelected.filter(v => !!v).sort((a, b) => String(a).localeCompare(String(b))).join(",");
30
+ if (!query) return true;
31
+ return !!pathDict.value[query];
32
+ };
33
+ const toggleValue = (specIndex, valueId) => {
34
+ if (selectedValueIds.value[specIndex] === valueId) {
35
+ selectedValueIds.value[specIndex] = "";
36
+ } else {
37
+ selectedValueIds.value[specIndex] = valueId;
38
+ }
39
+ };
40
+ const selectedSku = (0, _vue.computed)(() => {
41
+ const completeSelection = selectedValueIds.value.every(v => !!v);
42
+ if (!completeSelection || selectedValueIds.value.length < specs.length) return null;
43
+ const targetKey = [...selectedValueIds.value].sort((a, b) => String(a).localeCompare(String(b))).join(",");
44
+ return skus.find(sku => [...sku.specValueIds].sort((a, b) => String(a).localeCompare(String(b))).join(",") === targetKey) || null;
45
+ });
46
+ function getPowerSet(arr) {
47
+ const result = [[]];
48
+ for (const item of arr) {
49
+ const size = result.length;
50
+ for (let i = 0; i < size; i++) {
51
+ result.push([...result[i], item]);
52
+ }
53
+ }
54
+ return result.filter(v => v.length > 0).map(v => [...v].sort((a, b) => String(a).localeCompare(String(b))));
55
+ }
56
+ return {
57
+ selectedValueIds,
58
+ isValueSelectable,
59
+ selectedSku,
60
+ toggleValue
61
+ };
62
+ }
@@ -0,0 +1,30 @@
1
+ export interface SkuSpec {
2
+ id: string | number;
3
+ name: string;
4
+ values: SkuSpecValue[];
5
+ }
6
+ export interface SkuSpecValue {
7
+ id: string | number;
8
+ name: string;
9
+ [key: string]: unknown;
10
+ }
11
+ export interface SkuItem {
12
+ id: string | number;
13
+ specValueIds: Array<string | number>;
14
+ price: number;
15
+ stock: number;
16
+ [key: string]: unknown;
17
+ }
18
+ /**
19
+ * SKU 核心选择逻辑 Hook
20
+ * @description 基于幂集 (Power Set) 算法实现规格项状态联动和库存检查
21
+ * @param specs 规格列表
22
+ * @param skus SKU 列表
23
+ * @param initialSelection 初始选中的 ID
24
+ */
25
+ export declare function useSKU(specs: SkuSpec[], skus: SkuItem[], initialSelection?: Array<string | number>): {
26
+ selectedValueIds: import("vue").Ref<(string | number)[], (string | number)[]>;
27
+ isValueSelectable: (specIndex: number, valueId: string | number) => boolean;
28
+ selectedSku: import("vue").ComputedRef<SkuItem | null>;
29
+ toggleValue: (specIndex: number, valueId: string | number) => void;
30
+ };
@@ -0,0 +1,58 @@
1
+ import { ref, computed } from "vue";
2
+ export function useSKU(specs, skus, initialSelection = []) {
3
+ const selectedValueIds = ref(initialSelection);
4
+ const pathDict = computed(() => {
5
+ const dict = {};
6
+ skus.forEach((sku) => {
7
+ if (sku.stock <= 0) return;
8
+ const powerSet = getPowerSet(sku.specValueIds);
9
+ powerSet.forEach((path) => {
10
+ const key = path.join(",");
11
+ dict[key] = (dict[key] || 0) + sku.stock;
12
+ });
13
+ });
14
+ return dict;
15
+ });
16
+ const isValueSelectable = (specIndex, valueId) => {
17
+ const tempSelected = [...selectedValueIds.value];
18
+ if (tempSelected[specIndex] === valueId) {
19
+ tempSelected[specIndex] = "";
20
+ } else {
21
+ tempSelected[specIndex] = valueId;
22
+ }
23
+ const query = tempSelected.filter((v) => !!v).sort((a, b) => String(a).localeCompare(String(b))).join(",");
24
+ if (!query) return true;
25
+ return !!pathDict.value[query];
26
+ };
27
+ const toggleValue = (specIndex, valueId) => {
28
+ if (selectedValueIds.value[specIndex] === valueId) {
29
+ selectedValueIds.value[specIndex] = "";
30
+ } else {
31
+ selectedValueIds.value[specIndex] = valueId;
32
+ }
33
+ };
34
+ const selectedSku = computed(() => {
35
+ const completeSelection = selectedValueIds.value.every((v) => !!v);
36
+ if (!completeSelection || selectedValueIds.value.length < specs.length) return null;
37
+ const targetKey = [...selectedValueIds.value].sort((a, b) => String(a).localeCompare(String(b))).join(",");
38
+ return skus.find(
39
+ (sku) => [...sku.specValueIds].sort((a, b) => String(a).localeCompare(String(b))).join(",") === targetKey
40
+ ) || null;
41
+ });
42
+ function getPowerSet(arr) {
43
+ const result = [[]];
44
+ for (const item of arr) {
45
+ const size = result.length;
46
+ for (let i = 0; i < size; i++) {
47
+ result.push([...result[i], item]);
48
+ }
49
+ }
50
+ return result.filter((v) => v.length > 0).map((v) => [...v].sort((a, b) => String(a).localeCompare(String(b))));
51
+ }
52
+ return {
53
+ selectedValueIds,
54
+ isValueSelectable,
55
+ selectedSku,
56
+ toggleValue
57
+ };
58
+ }