shulan-quote 1.1.0 → 1.2.0

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.
package/index.html CHANGED
@@ -1,1498 +1,1497 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
6
- <meta name="theme-color" content="#1c4e80">
7
- <meta name="apple-mobile-web-app-capable" content="yes">
8
- <meta name="mobile-web-app-capable" content="yes">
9
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
10
- <meta name="apple-mobile-web-app-title" content="树懒智汇家报价系统">
11
- <meta name="application-name" content="树懒智汇家报价系统">
12
- <meta name="format-detection" content="telephone=no">
13
- <title>树懒智汇家报价系统</title>
14
- <link rel="manifest" href="manifest.webmanifest">
15
- <link rel="apple-touch-icon" href="apple-touch-icon.png">
16
- <link rel="icon" type="image/png" href="icon-192.png">
17
- <style>
18
-
19
- *{box-sizing:border-box;margin:0;padding:0}
20
- :root{
21
- --ink:#0f2136; --ink2:#41546b; --line:#e3e9f0; --bg:#f4f6f9;
22
- --brand:#1c4e80; --brand2:#2f6fb0; --accent:#0e7c66; --warn:#b4232a;
23
- --card:#fff; --radius:12px;
24
- }
25
- html,body{height:100%}
26
- body{
27
- font-family:"Microsoft YaHei UI","Segoe UI","PingFang SC",system-ui,sans-serif;
28
- color:var(--ink); background:var(--bg); display:flex; flex-direction:column; height:100vh; overflow:hidden;
29
- font-size:13.5px; -webkit-font-smoothing:antialiased;
30
- }
31
- /* ---------- header ---------- */
32
- header{
33
- display:flex; align-items:center; gap:14px; padding:0 18px; height:56px; flex:0 0 56px;
34
- background:linear-gradient(90deg,#153a5e,#1c4e80 60%,#2f6fb0); color:#fff; box-shadow:0 2px 10px rgba(15,33,54,.16);
35
- }
36
- .brand{display:flex; align-items:center; gap:9px; font-size:15px; font-weight:600; letter-spacing:.3px; white-space:nowrap}
37
- .brand .dot{width:9px;height:9px;border-radius:50%;background:#5fd3b0;box-shadow:0 0 0 3px rgba(95,211,176,.25)}
38
- .brand em{font-style:normal;font-size:11.5px;font-weight:400;color:#bcd4ea;letter-spacing:0}
39
- .search{flex:1; max-width:420px; position:relative}
40
- .search input{
41
- width:100%; height:34px; border-radius:8px; border:1px solid rgba(255,255,255,.22);
42
- background:rgba(255,255,255,.14); color:#fff; padding:0 34px 0 32px; font-size:13px; outline:none;
43
- }
44
- .search input::placeholder{color:#c3d6e8}
45
- .search input:focus{background:rgba(255,255,255,.22);border-color:rgba(255,255,255,.45)}
46
- .search .ico{position:absolute;left:10px;top:9px;font-size:13px;color:#c3d6e8}
47
- .search .clr{position:absolute;right:8px;top:6px;width:22px;height:22px;border:0;border-radius:50%;background:transparent;color:#cfe0ef;cursor:pointer;font-size:14px;display:none}
48
- .search .clr.on{display:block}
49
- .hdr-right{display:flex;align-items:center;gap:10px;margin-left:auto;white-space:nowrap}
50
- .fee{display:flex;align-items:center;gap:6px;font-size:12.5px;color:#d7e6f4;background:rgba(255,255,255,.12);padding:5px 10px;border-radius:8px}
51
- .fee input{width:46px;height:22px;border:0;border-radius:5px;background:rgba(255,255,255,.9);color:var(--ink);text-align:center;font-size:13px;font-weight:600;outline:none}
52
- .btn{height:32px;padding:0 13px;border-radius:8px;border:1px solid transparent;font-size:12.5px;cursor:pointer;font-family:inherit;transition:.15s}
53
- .btn.ghost{background:rgba(255,255,255,.14);color:#fff;border-color:rgba(255,255,255,.25)}
54
- .btn.ghost:hover{background:rgba(255,255,255,.26)}
55
- .btn.ghost.danger:hover{background:#8f1d22;border-color:#8f1d22}
56
- /* ---------- layout ---------- */
57
- main{flex:1; display:grid; grid-template-columns:196px 1fr 372px; min-height:0; overflow:hidden}
58
- /* ---------- nav ---------- */
59
- nav{background:#fff;border-right:1px solid var(--line);overflow-y:auto;padding:10px 8px 22px}
60
- .nav-t{font-size:11px;color:#8ba0b6;letter-spacing:1.2px;padding:8px 10px 6px;font-weight:600}
61
- .navi{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:9px;cursor:pointer;color:var(--ink2);margin-bottom:2px;position:relative;transition:.12s}
62
- .navi:hover{background:#eef3f9;color:var(--ink)}
63
- .navi.on{background:linear-gradient(90deg,#e7f0fa,#f2f7fc);color:var(--brand);font-weight:600}
64
- .navi.on::before{content:"";position:absolute;left:0;top:9px;bottom:9px;width:3px;border-radius:2px;background:var(--brand2)}
65
- .navi .idx{width:20px;height:20px;border-radius:6px;background:#e8eef5;color:#7c93aa;font-size:11px;display:flex;align-items:center;justify-content:center;font-weight:700;flex:0 0 20px}
66
- .navi.on .idx{background:var(--brand2);color:#fff}
67
- .navi .n{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
68
- .navi .c{font-size:11px;color:#9fb2c6;font-variant-numeric:tabular-nums}
69
- /* ---------- stage ---------- */
70
- #stage{display:flex;flex-direction:column;min-width:0;overflow:hidden;background:var(--bg)}
71
- .stage-top{flex:0 0 auto;padding:12px 16px 4px;background:#fff;border-bottom:1px solid var(--line)}
72
- .sh{display:flex;align-items:baseline;gap:10px;margin-bottom:9px}
73
- .sh h2{font-size:15px;font-weight:700;letter-spacing:.2px}
74
- .sh span{font-size:11.5px;color:#8ba0b6}
75
- .chips{display:flex;flex-wrap:wrap;gap:6px;padding-bottom:11px;max-height:96px;overflow-y:auto}
76
- .chip{padding:5px 11px;border-radius:14px;background:#f0f3f7;color:var(--ink2);font-size:12px;cursor:pointer;border:1px solid transparent;transition:.12s;white-space:nowrap}
77
- .chip:hover{background:#e4ebf4}
78
- .chip.on{background:var(--brand);color:#fff;border-color:var(--brand)}
79
- .chip .cn{opacity:.62;margin-left:4px;font-size:11px}
80
- .plist{flex:1;overflow-y:auto;padding:14px 16px 30px;display:grid;grid-template-columns:repeat(auto-fill,minmax(214px,1fr));grid-auto-rows:max-content;gap:10px;align-content:start}
81
- .pcard{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:11px 12px;cursor:pointer;position:relative;transition:.14s;overflow:hidden}
82
- .pcard:hover{border-color:#b9cee4;box-shadow:0 5px 16px rgba(28,78,128,.12);transform:translateY(-1px)}
83
- .pcard .pn{font-size:13px;font-weight:600;line-height:1.42;margin-bottom:7px;min-height:37px;word-break:break-all;padding-right:22px}
84
- .pcard .pm{display:flex;align-items:flex-end;justify-content:space-between}
85
- .pcard .pp{font-size:16.5px;font-weight:700;color:var(--brand);font-variant-numeric:tabular-nums}
86
- .pcard .pp small{font-size:11px;font-weight:400;color:#93a6ba;margin-left:2px}
87
- .pcard .add{width:24px;height:24px;border-radius:7px;background:#eef4fa;color:var(--brand2);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;font-weight:600;flex:0 0 24px;transition:.14s}
88
- .pcard:hover .add{background:var(--brand2);color:#fff}
89
- .pcard .alias{position:absolute;top:7px;right:7px;font-size:9.5px;background:#f0f7f4;color:var(--accent);border-radius:4px;padding:1px 4px;font-weight:600}
90
- .pcard.added{border-color:#a8d8c9;background:#f7fdfb}
91
- .empty{grid-column:1/-1;text-align:center;color:#9db0c4;padding:52px 0;font-size:13px}
92
- /* ---------- bill ---------- */
93
- #bill{background:#fff;border-left:1px solid var(--line);display:flex;flex-direction:column;min-width:0}
94
- .bill-head{flex:0 0 auto;padding:13px 15px 11px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:8px}
95
- .bill-head b{font-size:14px}
96
- .bill-head .cnt{font-size:11.5px;color:#8ba0b6;margin-left:auto;font-variant-numeric:tabular-nums}
97
- .billbody{flex:1;overflow-y:auto;padding:8px 12px 16px}
98
- .bgrp{margin-bottom:6px}
99
- .bgrp-h{display:flex;align-items:center;font-size:11.5px;color:#8ba0b6;padding:7px 3px 5px;font-weight:600;letter-spacing:.4px}
100
- .bgrp-h i{font-style:normal;width:5px;height:5px;border-radius:50%;background:#a9bdd2;margin-right:6px}
101
- .bgrp-h em{font-style:normal;margin-left:auto;font-variant-numeric:tabular-nums}
102
- .row{display:grid;grid-template-columns:1fr auto;gap:3px 6px;padding:8px 9px;border:1px solid #eef2f7;border-radius:9px;margin-bottom:5px;background:#fcfdfe}
103
- .row:hover{border-color:#dde7f1;background:#fbfdff}
104
- .row .rn{font-size:12.5px;line-height:1.4;grid-column:1/-1;word-break:break-all;padding-right:16px}
105
- .row .ru{font-size:11px;color:#9db0c4;grid-column:1/-1;margin-top:-1px}
106
- .row .calc{grid-column:1/-1;display:flex;align-items:center;gap:6px;margin-top:2px}
107
- .row input{border:1px solid #e1e8f0;border-radius:6px;height:26px;font-size:12.5px;text-align:center;font-family:inherit;color:var(--ink);outline:none;background:#fff;font-variant-numeric:tabular-nums}
108
- .row input:focus{border-color:var(--brand2);box-shadow:0 0 0 2px rgba(47,111,176,.12)}
109
- .row .ip{width:64px}
110
- .row .iq{width:52px}
111
- .row .x{font-size:11px;color:#a8b8c8}
112
- .row .amt{font-size:13px;font-weight:700;color:var(--brand);margin-left:auto;font-variant-numeric:tabular-nums}
113
- .row .del{position:absolute;opacity:0}
114
- .row{position:relative}
115
- .row .delbtn{position:absolute;top:6px;right:6px;width:18px;height:18px;border:0;border-radius:5px;background:transparent;color:#b9c8d6;cursor:pointer;font-size:13px;line-height:1;display:flex;align-items:center;justify-content:center}
116
- .row .delbtn:hover{background:#fdeaea;color:var(--warn)}
117
- .bempty{text-align:center;color:#a8b8c8;font-size:12.5px;padding:46px 20px;line-height:2}
118
- .bill-foot{flex:0 0 auto;border-top:1px solid var(--line);padding:12px 15px 14px;background:#fafcfe}
119
- .fl{display:flex;align-items:center;font-size:12.5px;color:var(--ink2);padding:3px 0}
120
- .fl em{font-style:normal;margin-left:auto;font-variant-numeric:tabular-nums;font-weight:600;color:var(--ink)}
121
- .fl.total{font-size:14px;padding-top:8px;margin-top:5px;border-top:1px dashed var(--line);color:var(--ink);font-weight:700}
122
- .fl.total em{font-size:20px;color:var(--brand);font-weight:800}
123
- .fl .tag{font-size:11px;color:#8ba0b6;margin-left:5px;font-weight:400}
124
- /* ---------- toast ---------- */
125
- #toast{position:fixed;left:50%;bottom:34px;transform:translate(-50%,20px);background:#0f2136;color:#fff;padding:9px 18px;border-radius:9px;font-size:12.5px;opacity:0;pointer-events:none;transition:.22s;z-index:99;box-shadow:0 8px 24px rgba(15,33,54,.3)}
126
- #toast.on{opacity:1;transform:translate(-50%,0)}
127
- /* ---------- scrollbar ---------- */
128
- ::-webkit-scrollbar{width:8px;height:8px}
129
- ::-webkit-scrollbar-thumb{background:#cfdae6;border-radius:4px}
130
- ::-webkit-scrollbar-thumb:hover{background:#b6c6d8}
131
- ::-webkit-scrollbar-track{background:transparent}
132
- /* ---------- responsive ---------- */
133
- @media(max-width:1180px){
134
- main{grid-template-columns:160px 1fr 330px}
135
- }
136
- @media(max-width:900px){
137
- body{font-size:13px}
138
- main{grid-template-columns:1fr;grid-template-rows:auto 1fr auto}
139
- nav{display:flex;overflow-x:auto;border-right:0;border-bottom:1px solid var(--line);padding:7px 8px;gap:5px}
140
- .nav-t{display:none}
141
- .navi{flex:0 0 auto;padding:7px 11px;margin:0;background:#f2f6fa}
142
- .navi .idx{display:none}
143
- .navi .c{display:none}
144
- .navi.on::before{display:none}
145
- #bill{max-height:44vh;border-left:0;border-top:1px solid var(--line)}
146
- .plist{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
147
- .hdr-right .fee{display:none}
148
- }
149
-
150
-
151
- /* ============ v7:树懒智汇家报价系统 追加样式 ============ */
152
- header .btn{background:#5fd3b0;color:#07301d;font-weight:700;border-color:transparent}
153
- header .btn:hover{background:#79e0c1}
154
- header .btn.ghost{background:rgba(255,255,255,.08);color:#dbe9e5;font-weight:400}
155
- header .btn.ghost:hover{background:rgba(255,255,255,.16);color:#fff}
156
-
157
- /* 产品卡:管理模式删除按钮 */
158
- .pcard .pdel{position:absolute;top:6px;right:6px;width:20px;height:20px;border-radius:6px;background:#fdeaea;color:#b4232a;border:0;font-size:13px;line-height:1;cursor:pointer;display:none;padding:0;z-index:3}
159
- .pcard .pdel:hover{background:#d94f4f;color:#fff}
160
- body.mgr .pcard .pdel{display:block}
161
- body.mgr .pcard .alias{display:none}
162
-
163
- /* 正在编辑的报价单提示条 */
164
- .editbar{display:none;align-items:center;gap:10px;padding:7px 16px;background:#0e7c66;color:#fff;font-size:12.5px;flex:0 0 auto}
165
- .editbar.on{display:flex}
166
- .editbar span{font-weight:600}
167
- .editbar .mini{margin-left:auto;height:24px;padding:0 10px;border-radius:6px;border:1px solid rgba(255,255,255,.45);background:transparent;color:#fff;font-size:12px;cursor:pointer;font-family:inherit}
168
- .editbar .mini:hover{background:rgba(255,255,255,.16)}
169
- .editbar .mini+.mini{margin-left:0}
170
- .editbar .mini.primary{background:#fff;color:#0b634f;border-color:#fff;font-weight:700}
171
-
172
- /* 模态框 */
173
- .modal{position:fixed;inset:0;z-index:90;background:rgba(9,24,36,.46);display:none;align-items:center;justify-content:center;padding:16px;backdrop-filter:blur(2px)}
174
- .modal.on{display:flex}
175
- .mbox{background:#fff;border-radius:14px;width:100%;max-width:620px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 60px rgba(9,24,36,.3)}
176
- .mbox.wide{max-width:1000px}
177
- .mhead{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
178
- .mhead b{font-size:14.5px}
179
- .mhead span{font-size:11.5px;color:#8ba0b6}
180
- .mx{margin-left:auto;width:26px;height:26px;border-radius:7px;border:1px solid var(--line);background:#fff;color:#7f93a8;font-size:15px;line-height:1;cursor:pointer;flex:0 0 auto}
181
- .mx:hover{background:#f2f6fa;color:var(--ink)}
182
- .mbody{padding:14px 16px;overflow:auto;flex:1}
183
- .mfoot{padding:10px 16px;border-top:1px solid var(--line);display:flex;align-items:center;gap:10px;font-size:12px;color:#8ba0b6;flex:0 0 auto}
184
- .mfoot .mini{margin-left:auto}
185
- .mbtn{display:block;width:100%;text-align:left;padding:11px 14px;border:1px solid var(--line);border-radius:9px;background:#fff;font-family:inherit;font-size:13px;color:var(--ink);cursor:pointer;margin-bottom:8px}
186
- .mbtn:hover{background:#f6fafd;border-color:#c9dcea}
187
- .modal.small .mbox{max-width:400px}
188
-
189
- /* 历史报价列表 */
190
- .hitem{display:flex;align-items:center;gap:10px;padding:11px 12px;border:1px solid var(--line);border-radius:10px;margin-bottom:8px}
191
- .hitem .hi{flex:1;min-width:0}
192
- .hitem .hn{font-size:13.5px;font-weight:600;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
193
- .hitem .hm{font-size:11.5px;color:#8ba0b6}
194
- .hitem .hs{font-size:14px;font-weight:800;color:var(--accent);font-variant-numeric:tabular-nums;white-space:nowrap}
195
- .hitem .ha{display:flex;gap:6px;flex:0 0 auto}
196
- .hitem .mini{height:28px;padding:0 10px;border-radius:7px;border:1px solid var(--line);background:#fff;font-family:inherit;font-size:12px;color:var(--ink2);cursor:pointer}
197
- .hitem .mini:hover{background:#f2f6fa;border-color:#c9dcea}
198
- .hitem .mini.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
199
- .hitem .mini.danger{color:#b4232a}
200
- .hitem .mini.danger:hover{background:#fdeaea;border-color:#f0c4c4}
201
- .hempty{text-align:center;color:#a8b8c8;font-size:12.5px;padding:44px 16px;line-height:2}
202
-
203
- /* 产品库管理 三栏 */
204
- .mgr3{display:grid;grid-template-columns:180px 180px 1fr;gap:10px;min-height:360px}
205
- .mcol{border:1px solid var(--line);border-radius:10px;display:flex;flex-direction:column;overflow:hidden;min-height:0}
206
- .mcol-h{display:flex;align-items:center;gap:6px;padding:7px 9px;background:#f7fafc;border-bottom:1px solid var(--line);font-size:12px;font-weight:700;color:var(--ink2);flex:0 0 auto}
207
- .mcol-h .mini{margin-left:auto;height:22px;padding:0 8px;border-radius:5px;border:1px solid var(--line);background:#fff;color:var(--brand);font-family:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
208
- .mcol-h .mini:hover{background:#eef5fb;border-color:#c9dcea}
209
- .mlist{overflow:auto;flex:1;padding:5px}
210
- .mli{display:flex;align-items:center;gap:5px;padding:6px 7px;border-radius:7px;font-size:12.5px;cursor:pointer;color:var(--ink2)}
211
- .mli:hover{background:#f4f8fc}
212
- .mli.on{background:#e8f2fb;color:var(--brand);font-weight:700}
213
- .mli .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
214
- .mli .num{font-size:10.5px;color:#a8b8c8;flex:0 0 auto}
215
- .mli .x{border:0;background:none;color:#c2cfdb;cursor:pointer;font-size:13px;padding:0 1px;flex:0 0 auto;line-height:1}
216
- .mli .x:hover{color:var(--warn)}
217
- .mtable{display:flex;flex-direction:column;overflow:auto;flex:1;padding:5px}
218
- .mtr{display:flex;align-items:center;gap:5px;padding:3px 5px;border-radius:6px;font-size:12px}
219
- .mtr:hover{background:#f7fafd}
220
- .mtr input{height:26px;border:1px solid var(--line);border-radius:5px;padding:0 6px;font-size:12px;font-family:inherit;color:var(--ink);min-width:0;outline:none;background:#fff}
221
- .mtr input:focus{border-color:var(--brand2);box-shadow:0 0 0 2px rgba(47,111,176,.1)}
222
- .mtr input.nm{flex:1}
223
- .mtr input.un{width:46px;text-align:center}
224
- .mtr input.pr{width:66px;text-align:right;font-variant-numeric:tabular-nums}
225
- .mtr .x{border:0;background:none;color:#c8d4e0;cursor:pointer;font-size:13px;padding:0 2px;line-height:1}
226
- .mtr .x:hover{color:var(--warn)}
227
- .mtr.mth{background:#f7fafc;font-size:11px;color:#8ba0b6;font-weight:700;position:sticky;top:0;z-index:2}
228
- .mtr.mth span:nth-child(1){flex:1}
229
- .mtr.mth span:nth-child(2){width:46px;text-align:center}
230
- .mtr.mth span:nth-child(3){width:66px;text-align:right}
231
- .mtr.mth span:nth-child(4){width:20px}
232
- .miniInput{height:24px;border:1px solid var(--line);border-radius:5px;padding:0 7px;font-size:11.5px;font-family:inherit;outline:none;width:96px;margin-left:auto}
233
- .addrow{display:flex;gap:5px;padding:7px;border-bottom:1px solid var(--line);background:#fafcfe;flex:0 0 auto;flex-wrap:wrap}
234
- .addrow input{height:27px;border:1px solid var(--line);border-radius:5px;padding:0 6px;font-size:12px;font-family:inherit;outline:none}
235
- .addrow input:first-child{flex:1;min-width:110px}
236
- .addrow input:nth-child(2){width:52px;text-align:center}
237
- .addrow input:nth-child(3){width:72px;text-align:right}
238
- .addrow .mini{height:27px;padding:0 10px;border-radius:5px;border:1px solid var(--line);background:#fff;font-family:inherit;font-size:12px;cursor:pointer}
239
- .addrow .mini.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
240
- .mini.danger{color:var(--warn);border-color:#f0c4c4;background:#fff;height:26px;padding:0 10px;border-radius:6px;font-family:inherit;font-size:12px;cursor:pointer}
241
- .billbtns{display:flex;gap:8px;margin-top:10px}
242
- .btn.block{flex:1;height:34px;border-radius:8px;border:1px solid var(--brand);background:var(--brand);color:#fff;font-size:13px;font-family:inherit;cursor:pointer;font-weight:600}
243
- .btn.block:hover{background:#16436e}
244
- .btn.ghost.block{background:#fff;color:var(--ink2);border-color:var(--line)}
245
- .btn.ghost.block:hover{background:#f6fafd}
246
- .btn.ghost.block.danger{color:var(--warn);border-color:#f0c4c4}
247
-
248
- /* 手机端 */
249
- @media(max-width:900px){
250
- header{height:auto;flex:0 0 auto;flex-wrap:wrap;padding:8px 11px;gap:7px}
251
- .brand{font-size:14px}
252
- .brand em{display:none}
253
- .search{order:3;max-width:none;flex:1 0 100%}
254
- .hdr-right{margin-left:0;flex:1 0 100%;gap:7px;overflow-x:auto;padding-bottom:1px}
255
- .hdr-right .fee{display:flex;flex:0 0 auto}
256
- .hdr-right .fee input{width:38px}
257
- header .btn{flex:0 0 auto;height:30px;padding:0 11px;font-size:12px}
258
- .mgr3{grid-template-columns:1fr;min-height:0}
259
- .mlist{max-height:150px}
260
- .mtable{max-height:260px}
261
- .mbox{max-height:94vh}
262
- .editbar{flex-wrap:wrap}
263
- .editbar span{flex:1 0 100%}
264
- .editbar .mini{margin-left:0}
265
- .hitem{flex-wrap:wrap}
266
- .hitem .ha{width:100%;justify-content:flex-end}
267
- }
268
- @media print{
269
- header,#nav,#stage,.editbar,.billbtns,#toast,.modal,.search{display:none!important}
270
- body{height:auto;overflow:visible;background:#fff}
271
- main{display:block}
272
- #bill{border:0}
273
- }
274
-
275
- /* 产品卡删除键:桌面悬停显示,手机隐藏防误触 */
276
- .pcard .pdel{opacity:0;transition:opacity .15s}
277
- .pcard:hover .pdel{opacity:1}
278
- @media(max-width:900px){.pcard .pdel{display:none}}
279
- /* 品牌名 */
280
- .bname{font-size:15px}
281
- @media(max-width:900px){.bname{font-size:14px}}
282
-
283
- /* ===== 云同步 ===== */
284
- #btnSync .sdot{ width:7px; height:7px; border-radius:50%; background:#9aa7b4; display:inline-block; margin-right:5px; vertical-align:1px; }
285
- #btnSync.sb-ok .sdot{ background:#17a673; }
286
- #btnSync.sb-err .sdot{ background:#e0483c; }
287
- #btnSync.sb-ing .sdot{ background:#f0a53c; }
288
- .cfgs{ padding:14px 16px; }
289
- .cfgs .cfgrow{ display:flex; align-items:center; margin:0 0 11px; }
290
- .cfgs .cfgrow label{ width:70px; flex:none; font-size:13px; color:#4a5a6a; }
291
- .cfgs .cfgrow input{ flex:1; min-width:0; padding:8px 10px; border:1px solid #d5dde5; border-radius:8px; font-size:13px; background:#fff; color:#22303c; }
292
- .cfgtip{ font-size:12px; line-height:1.65; color:#7b8896; background:#f4f7fa; border-radius:8px; padding:9px 11px; }
293
- .cst{ font-size:12px; color:#8b96a3; }
294
- .cst.ok{ color:#17a673; }
295
- .cst.err{ color:#e0483c; }
296
- .cst.ing{ color:#c9821f; }
297
-
298
- /* 云同步面板底部按钮:窄屏换行不挤压 */
299
- #mCloud .mfoot{ flex-wrap:wrap; gap:8px 6px; }
300
- #mCloud .mfoot .cst{ flex:1 0 100%; }
301
- #mCloud .mfoot .mini{ padding:7px 10px; font-size:12px; }
302
-
303
- </style>
304
- </head>
305
- <body>
306
- <header>
307
- <div class="brand"><span class="dot"></span><span class="bname">树懒智汇家报价系统</span><em id="stat"></em></div>
308
- <div class="search">
309
- <span class="ico">&#9906;</span>
310
- <input id="kw" placeholder="搜索产品 / 型号 / 品牌,如 射灯、C2、肤感">
311
- <button class="clr" id="kwClr">&times;</button>
312
- </div>
313
- <div class="hdr-right">
314
- <div class="fee">服务费率<input id="fee" type="number" value="25" min="0" max="100" step="1">%</div>
315
- <button class="btn ghost" id="btnHist">历史报价</button>
316
- <button class="btn ghost" id="btnMgr">产品库</button>
317
- <button class="btn ghost" id="btnSync" title="打开云同步设置"><i class="sdot"></i>云同步</button>
318
- <button class="btn" id="btnWord">导出 Word</button>
319
- <button class="btn ghost" id="btnMore">更多</button>
320
- </div>
321
- </header>
322
- <div id="editbar" class="editbar"><span id="ebText"></span>
323
- <button class="mini" id="ebSave">保存到本单</button>
324
- <button class="mini" id="ebNew">另存为新单</button>
325
- <button class="mini" id="ebExit">退出编辑</button>
326
- </div>
327
- <main>
328
- <nav id="nav"></nav>
329
- <section id="stage">
330
- <div class="stage-top">
331
- <div class="sh"><h2 id="catName"></h2><span id="catMeta"></span></div>
332
- <div class="chips" id="chips"></div>
333
- </div>
334
- <div class="plist" id="plist"></div>
335
- </section>
336
- <aside id="bill">
337
- <div class="bill-head"><b>报价明细</b><span class="cnt" id="cnt"></span></div>
338
- <div class="billbody" id="billbody"></div>
339
- <div class="bill-foot">
340
- <div class="fl"><span>产品小计<i class="tag" id="qtyTag"></i></span><em id="subAmt">&yen;0</em></div>
341
- <div class="fl"><span>服务费<i class="tag" id="feeTag">25%</i></span><em id="feeAmt">&yen;0</em></div>
342
- <div class="fl total"><span>合计</span><em id="totAmt">&yen;0</em></div>
343
- <div class="billbtns">
344
- <button class="btn block" id="btnSaveQuote">保存报价单</button>
345
- <button class="btn ghost block danger" id="btnClear">清空明细</button>
346
- </div>
347
- </div>
348
- </aside>
349
- </main>
350
-
351
- <!-- 历史报价 -->
352
- <div class="modal" id="mHist">
353
- <div class="mbox">
354
- <div class="mhead"><b>历史报价单</b><span>点“载入”可继续在原单上调整</span><button class="mx" data-close="mHist">&times;</button></div>
355
- <div class="mbody" id="histBody"></div>
356
- </div>
357
- </div>
358
-
359
- <!-- 产品库管理 -->
360
- <div class="modal" id="mMgr">
361
- <div class="mbox wide">
362
- <div class="mhead"><b>产品库管理</b><span>可新增/删除类目与子类、修改或删除产品,所有改动自动保存在本机</span>
363
- <button class="mx" data-close="mMgr">&times;</button></div>
364
- <div class="mbody mgr3">
365
- <div class="mcol">
366
- <div class="mcol-h"><b>类目</b><button class="mini" id="btnAddCat">+ 新增类目</button></div>
367
- <div class="mlist" id="catList"></div>
368
- </div>
369
- <div class="mcol">
370
- <div class="mcol-h"><b id="subTitle">子类</b><button class="mini" id="btnAddSub">+ 新增子类</button></div>
371
- <div class="mlist" id="subList"></div>
372
- </div>
373
- <div class="mcol grow">
374
- <div class="mcol-h"><b id="itemTitle">产品</b>
375
- <input id="mgrKw" class="miniInput" placeholder="筛选产品名">
376
- <button class="mini" id="btnAddItem">+ 新增产品</button>
377
- </div>
378
- <div class="addrow" id="addRow" style="display:none">
379
- <input id="anName" placeholder="产品名称">
380
- <input id="anUnit" value="个" title="单位">
381
- <input id="anPrice" type="number" min="0" placeholder="单价">
382
- <button class="mini primary" id="anSave">保存</button>
383
- <button class="mini" id="anCancel">取消</button>
384
- </div>
385
- <div class="mtable">
386
- <div class="mtr mth"><span>产品名称</span><span>单位</span><span>单价</span><span></span></div>
387
- <div id="itemRows"></div>
388
- </div>
389
- </div>
390
- </div>
391
- <div class="mfoot">
392
- <span id="mgrTip"></span>
393
- <button class="mini" id="btnExpDB" title="把当前产品库导出成 json 文件,换手机/换浏览器时用它恢复">导出备份</button>
394
- <button class="mini" id="btnImpDB" title="从备份文件恢复产品库">导入备份</button>
395
- <input type="file" id="impFile" accept=".json,application/json,text/plain" style="display:none">
396
- <button class="mini danger" id="btnResetDB">恢复初始产品库</button>
397
- </div>
398
- </div>
399
- </div>
400
-
401
- <!-- 云同步 -->
402
- <div class="modal" id="mCloud">
403
- <div class="mbox">
404
- <div class="mhead"><b>云同步</b><span>开启后,本机产品库的增删改会自动上传,其他人扫码进来会自动同步到最新</span><button class="mx" data-close="mCloud">&times;</button></div>
405
- <div class="mbody cfgs">
406
- <div class="cfgrow"><label>云端地址</label><input id="clUrl" placeholder="https://xxxx.supabase.co" autocapitalize="off" spellcheck="false"></div>
407
- <div class="cfgrow"><label>接口密钥</label><input id="clKey" placeholder="anon public key" autocapitalize="off" spellcheck="false"></div>
408
- <div class="cfgrow"><label>同步口令</label><input id="clRoom" placeholder="同一口令的人共用一份产品库,如 slzj-family" autocapitalize="off" spellcheck="false"></div>
409
- <div class="cfgtip" id="clTip">填好后点保存,会立即把本机产品库上传到云端;之后任何设备打开都会自动取最新版本。</div>
410
- </div>
411
- <div class="mfoot">
412
- <span id="clState"></span>
413
- <button class="mini" id="clPull">立即拉取云端</button>
414
- <button class="mini" id="clPush">上传本机版本</button>
415
- <button class="mini danger" id="clOff">关闭云同步</button>
416
- <button class="mini" id="clShare" title="生成带配置的分享链接,客户扫码打开即自动同步">生成分享链接</button>
417
- <button class="mini primary" id="clSave">保存并启用</button>
418
- </div>
419
- </div>
420
- </div>
421
-
422
- <!-- 更多 / 导出 -->
423
- <div class="modal small" id="mMore">
424
- <div class="mbox">
425
- <div class="mhead"><b>更多操作</b><button class="mx" data-close="mMore">&times;</button></div>
426
- <div class="mbody">
427
- <button class="mbtn" id="mWord">导出 Word 文档(.doc)</button>
428
- <button class="mbtn" id="mCsv">导出 CSV 表格</button>
429
- <button class="mbtn" id="mCopy">复制明细到剪贴板</button>
430
- <button class="mbtn" id="mPrint">打印 / 另存为 PDF</button>
431
- </div>
432
- </div>
433
- </div>
434
-
435
- <div id="toast"></div>
436
- <script>
437
- var DATA = {"cats": [{"n": "智能开关面板", "subs": ["小米智能开关", "肤感玻璃开关", "PC系列开关", "AG玻璃开关", "AE系列", "AK系列", "X6系列", "高端系列", "胡桃木系列", "无线开关", "插座与配件"]}, {"n": "智能照明", "subs": ["射灯", "筒灯", "灯带", "磁吸轨道灯", "驱动电源", "吸顶灯与灯泡", "灯槽型材"]}, {"n": "智能窗帘遮阳", "subs": ["窗帘电机", "窗帘轨道", "卷帘与百叶", "帘布面料", "电动开窗器", "窗帘配件"]}, {"n": "传感与安防", "subs": ["人体传感器", "环境传感器", "门窗与报警", "智能门锁", "摄像头监控"]}, {"n": "中控与语音", "subs": ["中控屏与场景屏", "智能音箱", "智能网关", "遥控器与支架"]}, {"n": "环境与家电", "subs": ["空调控制", "新风地暖", "浴霸暖风", "清洁家电", "晾衣架", "大家电"]}, {"n": "网络覆盖", "subs": ["路由器", "交换机", "AP组网"]}, {"n": "辅材与加工", "subs": ["低压电源", "辅材", "加工与物流"]}], "items": [[0, 0, "小米智能开关Pro(单键)", "个", 129.0], [0, 0, "小米智能开关Pro(双键)", "个", 149.0], [0, 0, "小米智能开关Pro(三键)", "个", 169.0], [0, 0, "米家智能家庭面板", "个", 185.0], [0, 0, "小米智能家庭面板(三键语音屏)(床头双键开关替换)", "个", 329.0], [0, 0, "小米智能家庭面板(零火三键)", "个", 329.0], [0, 0, "小米智能家庭面板(带三键开关)", "个", 329.0], [0, 0, "米家智能家庭面板(入门处开关)", "个", 329.0], [0, 0, "小米智能家庭面板(三键语音屏)", "个", 329.0], [0, 0, "xiaomi智能家庭面板屏pro8", "个", 749.0], [0, 1, "1.8(一块布单键)布帘", "个", 4.6], [0, 1, "2.38(一块布单键)纱帘", "个", 5.64], [0, 1, "开关刻字", "个", 7.0], [0, 1, "(纱帘大)5.695(一块布单键)布帘", "个", 12.53], [0, 1, "智能开关(单键单火版)", "个", 59.0], [0, 1, "智能开关(双键单火版)", "个", 64.0], [0, 1, "智能开关(三键单火版)", "个", 69.0], [0, 1, "米家智能开关(零火单键)肤感", "个", 89.0], [0, 1, "玻璃超薄肤感", "个", 129.0], [0, 1, "智能开关(零火单键)", "个", 79.0], [0, 1, "智能开关(单火单键)", "个", 79.0], [0, 1, "米家智能开关(零火双键)肤感", "个", 99.0], [0, 1, "玻璃超薄肤感", "个", 139.0], [0, 1, "智能开关(零火双键)", "个", 89.0], [0, 1, "智能开关(单火双键)", "个", 89.0], [0, 1, "肤感超薄智能开关(零火单键)", "个", 89.0], [0, 1, "米家智能开关(零火三键)肤感", "个", 109.0], [0, 1, "玻璃超薄肤感", "个", 149.0], [0, 1, "智能开关(零火三键)", "个", 99.0], [0, 1, "智能开关", "个", 99.0], [0, 1, "智能开关(单火三键)", "个", 99.0], [0, 1, "肤感超薄智能开关(零火双键)", "个", 99.0], [0, 1, "肤感超薄智能开关(零火三键)", "个", 109.0], [0, 1, "智能开关(零火四键)", "个", 119.0], [0, 1, "肤感超薄智能开关(零火四键)", "个", 119.0], [0, 1, "肤感玻璃(单火单键)", "个", 129.0], [0, 1, "肤感玻璃(零火单键)", "个", 129.0], [0, 1, "肤感玻璃智能开关(单键)白色", "个", 129.0], [0, 1, "肤感玻璃(单火双键)", "个", 139.0], [0, 1, "肤感玻璃(零火双键)", "个", 139.0], [0, 1, "肤感玻璃智能开关(双键)白色", "个", 139.0], [0, 1, "肤感玻璃(单火三键)", "个", 149.0], [0, 1, "肤感玻璃(零火三键)", "个", 149.0], [0, 1, "肤感玻璃智能开关(三键)白色", "个", 149.0], [0, 1, "肤感玻璃(零火四键)", "个", 159.0], [0, 1, "肤感玻璃智能开关(四键)白色", "个", 159.0], [0, 1, "玻璃超薄肤感智能开关(单火四键)", "个", 169.0], [0, 1, "四键两场景圆屏", "个", 399.0], [0, 1, "智能面板", "个", 649.0], [0, 1, "新风旋钮面板", "个", 899.0], [0, 2, "PC材质智能开关(零火单键)", "个", 69.0], [0, 2, "PTXPC开关(单火单键)", "个", 69.0], [0, 2, "PTXPC开关(单火双键)", "个", 74.0], [0, 2, "PC材质智能开关(零火双键)", "个", 79.0], [0, 2, "PTXPC开关(单火三键)", "个", 79.0], [0, 2, "PC肤感超薄(零火单键)", "个", 79.0], [0, 2, "PC材质智能开关(零火三键)", "个", 89.0], [0, 2, "PC肤感超薄(零火双键)", "个", 89.0], [0, 2, "PC肤感(单键)", "个", 89.0], [0, 2, "PC肤感超薄(零火三键)", "个", 99.0], [0, 2, "PC肤感(双键)", "个", 99.0], [0, 2, "PC肤感超薄(零火四键)", "个", 109.0], [0, 2, "PC肤感(三键)", "个", 109.0], [0, 3, "AG玻璃(零火单键)", "个", 129.0], [0, 3, "AG智能开关(单键)(单火版)", "个", 129.0], [0, 3, "AG智能开关(单键)", "个", 129.0], [0, 3, "AG智能开关(双键零火版)", "个", 139.0], [0, 3, "AG智能开关", "个", 139.0], [0, 3, "AG智能开关(双键)", "个", 139.0], [0, 3, "AG智能开关(双键)(单火版)", "个", 144.0], [0, 3, "AG玻璃(零火三键)", "个", 149.0], [0, 3, "AG智能开关(三键)(单火版)", "个", 149.0], [0, 3, "AG智能开关(三键)", "个", 149.0], [0, 3, "AG玻璃(零火四键)", "个", 159.0], [0, 3, "AG玻璃超薄肤感智能开关(零火单键)", "个", 159.0], [0, 3, "AG玻璃超薄肤感智能开关(零火双键)", "个", 169.0], [0, 3, "AG玻璃超薄肤感智能开关(零火三键)", "个", 179.0], [0, 4, "AE智能开关(单键)(零火版)", "个", 70.0], [0, 4, "AE智能开关(双键)(零火版)", "个", 72.5], [0, 4, "平头熊AE(单火单键)", "个", 79.0], [0, 4, "AE智能开关(单键)", "个", 79.0], [0, 4, "平头熊AE(单火双键)", "个", 89.0], [0, 4, "AE智能开关(双键)", "个", 89.0], [0, 4, "AE智能开关(三键)(零火版)", "个", 90.0], [0, 4, "平头熊AE(单火三键)", "个", 99.0], [0, 4, "AE智能开关(三键开)", "个", 99.0], [0, 4, "AE智能开关(四键)(零火版)", "个", 100.0], [0, 5, "AK智能开关(单键)", "个", 44.0], [0, 5, "平头熊AK(零火单键)", "个", 49.0], [0, 5, "AK智能开关(双键)", "个", 54.0], [0, 5, "平头熊AK(零火双键)", "个", 59.0], [0, 5, "智能开关AK(单键单火版)灰色", "个", 59.0], [0, 5, "智能开关AK(双键单火版)灰色", "个", 64.0], [0, 5, "AK智能开关(三键)", "个", 64.0], [0, 5, "平头熊AK(零火三键)", "个", 69.0], [0, 5, "AK智能开关(零火单键)", "个", 69.0], [0, 5, "智能开关AK(三键单火版)灰色", "个", 69.0], [0, 5, "AK智能开关(零火三键)", "个", 89.0], [0, 5, "平头熊AKPRO(零火单键)", "个", 129.0], [0, 5, "平头熊AKPRO(零火双键)", "个", 149.0], [0, 5, "平头熊AKPRO(零火三键)", "个", 169.0], [0, 6, "X6智能开关(单键)(零火版)", "个", 129.0], [0, 6, "X6智能开关(双键)(零火版)", "个", 139.0], [0, 6, "X6智能开关(三键)(零火版)", "个", 149.0], [0, 6, "X6旋转调光开关", "个", 150.0], [0, 6, "平头熊X6单键开关", "个", 170.0], [0, 6, "平头熊X6双键开关", "个", 180.0], [0, 6, "X6(零火版三键开关)", "个", 190.0], [0, 6, "平头熊X6三键开关", "个", 190.0], [0, 6, "X6(零火版6键场景开关)", "个", 269.0], [0, 7, "绿米Z1(零火单键)", "个", 148.0], [0, 7, "绿米Z1(零火双键)", "个", 168.0], [0, 7, "X1PRO(零火单键)", "个", 179.0], [0, 7, "绿米Z1(零火三键)", "个", 188.0], [0, 7, "X1PRO(零火双键)", "个", 189.0], [0, 7, "X1PRO(零火三键)", "个", 199.0], [0, 7, "绿米Z1(单零火通用四键)", "个", 208.0], [0, 7, "X1PRO(零火四键)", "个", 209.0], [0, 8, "胡桃木智能开关(零火单键)", "个", 129.0], [0, 8, "胡桃木智能开关(零火双键)", "个", 139.0], [0, 8, "胡桃木智能开关(零火三键)", "个", 149.0], [0, 8, "胡桃木智能开关(零火四键)", "个", 159.0], [0, 9, "小米无线智能开关贴", "个", 20.0], [0, 9, "小米无线智能开关贴(场景按键)", "个", 29.9], [0, 9, "无线圆钮开关", "个", 35.0], [0, 9, "无线开关AK(单键)灰色", "个", 40.0], [0, 9, "无线开关双键白色", "个", 45.0], [0, 9, "无线开关(单键)", "个", 45.0], [0, 9, "无线开关", "个", 45.0], [0, 9, "无线开关(单键小米灰)", "个", 45.0], [0, 9, "无线开关AK(双键)灰色", "个", 45.0], [0, 9, "无线开关三键白色", "个", 47.5], [0, 9, "无线智能开关(双键)", "个", 50.0], [0, 9, "无线开关AK(三键)灰色", "个", 50.0], [0, 9, "无线智能开关(三键)", "个", 55.0], [0, 9, "无线开关单键", "个", 55.0], [0, 9, "AK无线开关(单键)(无线开关)", "个", 55.0], [0, 9, "AK无线开关(双键)(无线开关)", "个", 65.0], [0, 9, "无线开关(双键)", "个", 67.0], [0, 9, "窗帘无线开关控制器(开/停/关)", "个", 69.0], [0, 9, "X6无线调光情景开关", "个", 140.0], [0, 10, "肤感玻璃五孔插座", "个", 69.0], [0, 10, "智能插座", "个", 89.0], [1, 0, "智能射灯", "个", 129.0], [1, 0, "轨道射灯(暗装款)10w", "个", 129.0], [1, 0, "(满星思维/征服者)75mm智能调光射灯", "盏", 129.0], [1, 0, "12w射灯", "个", 129.0], [1, 0, "米家智能射灯", "个", 129.0], [1, 0, "易来智能射灯C3(12W-36°)", "个", 129.0], [1, 0, "射灯7w24°", "个", 129.0], [1, 0, "射灯7w36°", "个", 129.0], [1, 0, "射灯9w36°", "个", 129.0], [1, 0, "智能筒灯(射灯60度)", "个", 129.0], [1, 0, "米家智能明装射灯", "个", 136.0], [1, 0, "7w-10w-12w智能射灯", "个", 139.0], [1, 0, "智能射灯(55度)", "个", 149.0], [1, 0, "智能射灯(36度)", "个", 149.0], [1, 0, "明装智能射灯", "个", 149.0], [1, 0, "磁吸轨道射灯", "个", 169.0], [1, 0, "平头熊X6智能射灯8w24°", "个", 169.0], [1, 0, "平头熊X6智能射灯24°", "个", 169.0], [1, 0, "平头熊X6智能射灯36°", "个", 169.0], [1, 0, "平头熊X6智能射灯15°", "个", 169.0], [1, 0, "绿米T3-36°射灯", "个", 169.0], [1, 0, "智能射灯36°12w2700-6500色温调节-0%-100%亮度调节", "个", 169.0], [1, 0, "智能射灯24°12w2700-6500色温调节-0%-100%亮度调节", "个", 169.0], [1, 0, "智能射灯(12w-RA98)", "个", 179.0], [1, 0, "(满星思维/黑骑士)75mm智能调光射灯", "盏", 189.0], [1, 0, "射灯H1", "个", 189.0], [1, 0, "嵌入式轨道射灯20w", "个", 269.0], [1, 1, "7w筒灯", "个", 89.0], [1, 1, "筒灯10w60°", "个", 89.0], [1, 1, "智能筒灯10w", "个", 89.0], [1, 1, "三雄极光12w筒灯", "个", 99.0], [1, 1, "智能筒灯", "个", 99.0], [1, 1, "7w-10w智能筒灯90-95开孔", "个", 109.0], [1, 1, "易来智能筒灯C3(10W-55°)", "个", 129.0], [1, 1, "75开孔智能筒灯", "个", 129.0], [1, 1, "易来智能筒灯S1(10W-90°)", "个", 129.0], [1, 1, "平头熊智能筒灯60°", "个", 149.0], [1, 1, "明装筒灯12w", "个", 159.0], [1, 1, "筒灯H1", "个", 169.0], [1, 2, "灯带接驳配件", "米", 16.0], [1, 2, "柜体灯带", "米", 22.0], [1, 2, "双色温灯带(10米/卷)", "米", 22.0], [1, 2, "智能cob调光灯带", "米", 26.0], [1, 2, "智能双色温全光谱调光灯带19.5", "米", 30.0], [1, 2, "灯带", "米", 35.0], [1, 2, "智能双色温灯带8mm2700-6500色温调节-0%-100%亮度调节", "米", 35.0], [1, 2, "cob灯带-2700-6500k(12w/米)", "米", 35.0], [1, 2, "cob无极调光灯带", "米", 35.0], [1, 2, "智能COB全光谱双色温调光灯带", "米", 35.0], [1, 2, "智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)", "米", 37.0], [1, 2, "裸板双色温智能灯带(", "米", 43.0], [1, 2, "智能COB调光灯带", "米", 45.0], [1, 2, "智能COB灯带(RA97-2700k-6500k色温调节0%-100%亮度调节)", "米", 45.0], [1, 2, "无边线性灯型材(含灯带)", "米", 50.0], [1, 2, "轨道泛光灯(暗装款)10w", "个", 76.0], [1, 2, "轨道格栅灯(暗装款)12w", "个", 119.0], [1, 2, "轨道吊线灯10w", "个", 140.0], [1, 2, "智能灯带", "盘/10米", 189.0], [1, 2, "双色温灯带10米", "10米/卷", 189.0], [1, 2, "三雄极光灯带", "10米/盘", 189.0], [1, 2, "智能调光COB灯带5m一盘", "个", 240.0], [1, 2, "嵌入式格栅灯", "个", 245.0], [1, 2, "智能灯带控制器", "个", 269.0], [1, 2, "双色温cob灯带H1", "盘/5米", 299.0], [1, 2, "床头灯带套餐", "个", 899.0], [1, 3, "磁吸轨道导电接头", "个", 25.0], [1, 3, "磁吸轨道直连模块", "个", 39.0], [1, 3, "嵌入式磁吸直线连接器", "个", 39.0], [1, 3, "明装磁吸轨道", "米", 50.0], [1, 3, "明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)", "米", 80.0], [1, 3, "磁吸轨道", "米", 89.0], [1, 3, "嵌入式磁吸轨道", "米", 90.0], [1, 3, "磁吸轨道格栅灯", "个", 149.0], [1, 3, "12W磁吸防眩格栅灯", "个", 165.0], [1, 3, "磁吸轨道灯(泛光灯)", "个", 179.0], [1, 3, "嵌入式磁吸泛光灯", "30cm", 199.0], [1, 3, "智能磁吸格栅灯H1", "个", 199.0], [1, 3, "磁吸轨道灯(聚光灯)", "个", 239.0], [1, 3, "嵌入式磁吸格栅灯-12头", "30cm", 289.0], [1, 3, "智能磁吸格泛光灯H160cm", "个", 289.0], [1, 4, "无线小圆钮", "个", 20.0], [1, 4, "64G内存卡", "个", 39.0], [1, 4, "弯轨转接头", "个", 60.0], [1, 4, "玻璃胶", "个", 62.5], [1, 4, "筒射灯驱动", "个", 72.0], [1, 4, "智能灯带调光驱动器", "个", 79.0], [1, 4, "U型弯轨费", "个", 80.0], [1, 4, "小米智能摄像头75mm开孔嵌入器及电源", "个", 85.0], [1, 4, "60w低压电源", "个", 109.0], [1, 4, "智能灯带电源120w", "个", 109.0], [1, 4, "100w24v变压器(层板)两线单色温", "个", 125.0], [1, 4, "灯带一体式驱动电源150瓦", "个", 129.0], [1, 4, "智能调光灯带电源200w", "个", 130.0], [1, 4, "灯带调光驱动", "个", 135.0], [1, 4, "卡农Z1", "个", 139.0], [1, 4, "色温灯带智能驱动120w", "个", 149.0], [1, 4, "60w24v杜邦接口变压器", "个", 150.0], [1, 4, "智能灯带电源300w", "个", 169.0], [1, 4, "灯带电源驱动", "个", 169.0], [1, 4, "(满星思维/灯带调光电源驱动器)120w", "个", 169.0], [1, 4, "柜体专用调光驱动(米家)150w", "个", 175.0], [1, 4, "100w低压电源", "个", 179.0], [1, 4, "智能调光一体式电源驱动", "个", 189.0], [1, 4, "灯带一体式电源驱动200w", "个", 189.0], [1, 4, "米家一体式驱动电源", "个", 189.0], [1, 4, "小米BE3600", "个", 219.0], [1, 4, "智能300w电源驱动调光控制器", "个", 225.0], [1, 4, "150w低压电源", "个", 229.0], [1, 4, "灯带一体式驱动电源300瓦", "个", 250.0], [1, 4, "智能灯带电源驱动300w", "个", 265.0], [1, 4, "吊灯调光器", "个", 267.5], [1, 4, "绿米S1E", "个", 269.0], [1, 4, "妙控S1E", "个", 269.0], [1, 4, "小米be5000", "个", 289.0], [1, 4, "AX3000", "个", 289.0], [1, 4, "小米ax3000", "个", 329.0], [1, 4, "磁吸轨道电源H1", "条", 350.0], [1, 4, "嵌入式磁吸在轨电源", "个", 350.0], [1, 4, "X6液晶旋钮主机", "个", 369.0], [1, 4, "中枢无关", "个", 399.0], [1, 4, "AX6000", "个", 439.0], [1, 4, "小米aX6000", "个", 469.0], [1, 4, "BE6500", "个", 499.0], [1, 4, "1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)", "个", 599.0], [1, 4, "xiaomisound", "个", 599.0], [1, 4, "X6液晶旋扭主机", "个", 599.0], [1, 4, "tplink", "个", 1200.0], [1, 4, "小米ax9000", "个", 1299.0], [1, 4, "AX9000", "个", 1299.0], [1, 4, "72英寸16:9天花幕(4k高清纳米白玻纤)", "个", 1799.0], [1, 4, "鹿客S50MPRO", "个", 1849.0], [1, 4, "绿米H100", "个", 2799.0], [1, 4, "鹿客V5MAX", "个", 3049.0], [1, 4, "米家十字门521L超薄嵌入式", "个", 3499.0], [1, 4, "绿米D200", "个", 4398.0], [1, 5, "COB双色温灯带(电视背景墙)4*2", "米", 37.0], [1, 5, "米家智能灯泡e14", "个", 45.0], [1, 5, "E14智能灯泡", "个", 55.0], [1, 5, "电视红外语控器", "米", 69.0], [1, 5, "吸顶灯L1", "个", 219.0], [1, 5, "吸顶灯(卧室)36w", "个", 324.8], [1, 5, "智能天际灯(RA90-3000k-6000k色温调节0%-100%亮度调节)", "套/4m", 425.0], [1, 5, "智能吸顶灯50w", "个", 557.0], [1, 5, "吸顶灯96w", "个", 778.0], [1, 6, "16*16三角灯槽", "米", 8.6], [1, 6, "U型型材", "米", 16.0], [1, 6, "灯槽", "米", 23.0], [1, 6, "灯槽(悬浮吊顶灯槽)", "米", 60.0], [2, 0, "CM窗帘电机(双轨)", "个", 175.0], [2, 0, "CM窗帘电机(单轨)", "个", 175.0], [2, 0, "mesh智能窗帘电机", "个", 189.0], [2, 0, "杜亚静音款mesh智能窗帘电机", "个", 299.0], [2, 0, "窗帘电机", "个", 299.0], [2, 0, "静音窗帘电机", "个", 299.0], [2, 0, "平头熊窗帘电机", "个", 299.0], [2, 0, "(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "个", 299.0], [2, 0, "静音款窗帘电机(双弯轨)", "个", 299.0], [2, 0, "智能窗帘电机S6", "个", 299.0], [2, 0, "窗帘电机(单轨双电机)", "个", 299.0], [2, 0, "杜亚静音款mesh智能窗帘电机(弯轨)", "个", 299.0], [2, 0, "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)", "个", 480.0], [2, 0, "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)", "个", 480.0], [2, 0, "窗帘电机(含3米轨道)", "个", 499.0], [2, 0, "杜亚静音款mesh智能窗帘电机大扭力", "个", 499.0], [2, 0, "杜亚静音款mesh智能窗帘电机(含3米轨道)", "个", 559.0], [2, 0, "25卷帘电机", "个", 599.0], [2, 0, "杜亚35卷帘电机", "个", 699.0], [2, 0, "窗帘电机C2(含3米轨道)(纱帘2.2米)", "个", 1098.0], [2, 1, "1.6静音电动窗帘轨道", "米", 50.0], [2, 1, "杜亚静音款1.6厚窗帘轨道", "米", 60.0], [2, 1, "电机静音导轨/米2.0厚", "米", 80.0], [2, 1, "窗帘轨道(单轨单电机)", "米", 80.0], [2, 1, "窗帘盒", "米", 80.0], [2, 1, "窗帘轨道2.0(静音款)弯轨", "米", 80.0], [2, 1, "窗帘轨道", "米", 80.0], [2, 1, "2.0静音窗帘轨道", "米", 80.0], [2, 1, "窗帘轨道2.0(静音款)直轨", "米", 80.0], [2, 1, "静音窗帘轨道(双轨)", "米", 80.0], [2, 1, "静音窗帘轨道(1单轨)", "米", 80.0], [2, 1, "窗帘轨道(弯轨)", "米", 80.0], [2, 1, "杜亚静音款2.0厚窗帘轨道(弯轨)6.91+0.539", "米", 80.0], [2, 1, "窗帘轨道(双轨)", "米", 89.0], [2, 1, "铝合金窗帘盒", "米", 110.0], [2, 1, "杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4", "米", 125.0], [2, 2, "铝百叶", "平方", 88.0], [2, 2, "百叶帘电机(杜亚可接入米家微调)", "个", 550.0], [2, 2, "木百叶35管状智能电机", "个", 699.0], [2, 3, "卷帘面料", "平方", 80.0], [2, 3, "梦幻帘布料及轨道2.75高", "平方", 135.0], [2, 3, "日夜帘布料1.8*1.7", "平方", 185.0], [2, 4, "电动开窗器", "套/单扇窗户", 859.0], [2, 5, "电动窗帘(双轨卧室5)", "米", 65.0], [2, 5, "电动窗帘(单轨)", "米", 65.0], [2, 5, "2.0静音电机轨道", "米", 80.0], [2, 5, "轨道超过3米按80/米增加", "个", 83.0], [2, 5, "梦幻帘轨道", "个", 120.0], [2, 5, "日夜帘=1.139*1.775", "平方", 195.0], [2, 5, "静音电机", "个", 299.0], [2, 5, "智能窗帘系统", "个", 437.82], [2, 5, "智能窗帘大扭力电机mesh静音款", "个", 450.0], [2, 5, "日夜帘电机(上下两层单电机)", "台", 499.0], [2, 5, "窗帘(电机含3米轨道)", "套", 599.0], [2, 5, "米家隐藏式智能窗帘(含3米轨道)", "个", 599.0], [2, 5, "杜亚静音款mesh日夜帘智能升降电机", "个", 650.0], [2, 5, "智能升降电机", "个", 699.0], [2, 5, "香格里拉帘电机", "个", 720.0], [3, 0, "领普人体感应器", "个", 69.0], [3, 0, "小米人体传感器2S", "个", 69.0], [3, 0, "人体感应器", "个", 89.0], [3, 0, "领普人体存在感应器(,公卫)如干湿分离则需增加", "个", 119.0], [3, 0, "小米人体存在感应器", "个", 121.0], [3, 0, "嵌入式人体存在感应器", "个", 129.0], [3, 0, "领普顶装人体存在传感器", "个", 129.0], [3, 0, "人体存在感应器无线", "个", 129.0], [3, 0, "人体存在传感器", "个", 149.0], [3, 0, "人体存在感应器顶装", "个", 149.0], [3, 0, "领普人体存在感应器", "个", 149.0], [3, 0, "人体存在感应器(干区及)95开孔", "个", 149.0], [3, 0, "毫米波雷达人体存在感应器", "个", 149.0], [3, 0, "绿米人体存在感应器", "个", 369.0], [3, 1, "门磁感应器", "个", 49.0], [3, 1, "水浸传感器", "个", 69.0], [3, 1, "小米门窗感应器", "个", 69.0], [3, 1, "门窗感应器", "个", 89.0], [3, 1, "温湿度感应器", "个", 119.0], [3, 1, "烟雾传感器", "个", 149.0], [3, 1, "小米智能摄像机云台版2k", "个", 199.0], [3, 1, "小米智能摄像机2云台版", "个", 269.0], [3, 2, "水浸报警器", "个", 59.0], [3, 2, "烟雾报警器", "个", 149.0], [3, 2, "霸王锁体", "个", 220.0], [3, 3, "小米智能门锁2", "个", 934.0], [3, 4, "摄像头磁吸底座", "个", 80.0], [3, 4, "小米摄像头3云台版", "个", 199.0], [3, 4, "cw500室外摄像头", "个", 349.0], [3, 4, "摄像头", "个", 599.0], [4, 0, "小米家庭屏pro8上墙支架", "个", 69.9], [4, 0, "家庭屏上墙支架", "个", 85.0], [4, 0, "过道中控屏上墙充电支架", "个", 110.0], [4, 0, "AG智能屏显开关(月壤灰)(零火双键)", "个", 160.0], [4, 0, "AG智能屏显开关(月壤灰)(零火三键)", "个", 176.0], [4, 0, "Xiaomi智能家庭屏Mini", "个", 249.0], [4, 0, "4寸场景屏", "个", 269.0], [4, 0, "4英寸场景屏", "个", 269.0], [4, 0, "智能安防", "个", 269.0], [4, 0, "AK智能屏4英寸", "个", 269.0], [4, 0, "4寸智能场景控制屏", "个", 269.0], [4, 0, "4寸超薄中控屏开关", "个", 348.0], [4, 0, "小米语控屏幕", "个", 349.0], [4, 0, "X6中控屏", "个", 379.0], [4, 0, "小米sound音响", "个", 458.0], [4, 0, "pro8中控屏", "个", 689.0], [4, 0, "小米pro8智能屏", "个", 749.0], [4, 0, "家庭中控大屏pro8", "个", 749.0], [4, 0, "8寸中控屏", "个", 749.0], [4, 0, "智能家庭中控屏", "个", 799.0], [4, 0, "中央集控屏", "个", 799.0], [4, 0, "集悦妙控屏S1PLUS", "个", 1899.0], [4, 1, "小爱音箱play", "个", 99.0], [4, 1, "智能音箱", "个", 269.0], [4, 1, "小米音箱pro", "个", 269.0], [4, 1, "吸顶音箱", "个", 606.64], [4, 2, "多模网关", "个", 229.0], [4, 2, "中枢网关", "个", 349.0], [4, 2, "小米中枢网关", "个", 349.0], [4, 2, "网关M2", "个", 389.0], [4, 2, "绿米M2网关", "个", 399.0], [4, 3, "单频遥控器", "个", 30.0], [4, 3, "双频遥控器", "个", 40.0], [5, 0, "16A挂机控制器", "个", 89.0], [5, 0, "小米空调伴侣", "个", 89.0], [5, 0, "柜机空调控制器", "个", 169.0], [5, 0, "空调伴侣p3", "个", 239.0], [5, 0, "风管机控制器WIFI:350mesh:699", "个", 350.0], [5, 0, "米家温控伴侣", "个", 479.0], [5, 0, "风管机控制", "个", 599.0], [5, 0, "风管机内置控制器", "个", 699.0], [5, 0, "风管机内置控制网关mesh", "个", 699.0], [5, 0, "中央空调控制器", "个", 1299.0], [5, 0, "中央空调VRF(mesh增强版)", "个", 1599.0], [5, 0, "中央空调内置控制网关", "个", 1699.0], [5, 0, "中央空调vfr", "个", 1799.0], [5, 0, "小米空调1.5匹巨省电", "个", 1999.0], [5, 1, "新风内置控制器", "个", 1099.0], [5, 1, "地暖控制器", "个", 1299.0], [5, 2, "小米浴霸pro安装", "个", 60.0], [5, 2, "浴霸转换框", "个", 80.0], [5, 2, "米家智能浴霸转换框", "个", 83.0], [5, 2, "浴霸", "个", 449.0], [5, 2, "米家智能浴霸", "个", 499.0], [5, 2, "小米浴霸pro", "个", 699.0], [5, 2, "米家智能浴霸S1", "个", 899.0], [5, 2, "绿米浴霸T1", "个", 1399.0], [5, 3, "小米空气净化器5s", "个", 1539.0], [5, 3, "米家全能扫拖机器人M30S", "台", 2999.0], [5, 3, "米家全能扫地机器人M30PRO", "台", 4699.0], [5, 4, "米家智能晾衣架1s", "个", 1059.0], [5, 4, "智能晾衣架", "个", 1299.0], [5, 4, "米家智能晾衣架Pro", "个", 1299.0], [5, 5, "米家零热水燃气热水器18L", "个", 2199.0], [5, 5, "小米极光投影仪1S", "台", 5599.0], [6, 0, "WIFI-面板", "个", 25.0], [6, 0, "WIFI8口交换机", "个", 129.0], [6, 0, "WIFIax3000(小米)", "个", 299.0], [6, 0, "WIFI-锐捷4口千兆交换机", "个", 300.0], [6, 0, "米家路由器BE5000", "个", 319.0], [6, 0, "WiFi(小米万兆交换机)", "个", 349.0], [6, 0, "WIFI-ap162e", "个", 349.0], [6, 0, "WiFi-AX6000", "个", 399.0], [6, 0, "WIFIaX6000(小米)", "个", 429.0], [6, 0, "WiFi(有线mesh)(小米)", "个", 439.0], [6, 0, "WiFi", "个", 479.0], [6, 0, "WIFIBE6500", "个", 499.0], [6, 0, "WIFI覆盖", "个", 659.0], [6, 0, "WIFI主路由BE6500PRO", "个", 659.0], [6, 0, "米家路由器BE7000", "个", 799.0], [6, 0, "WiFi-AX9000", "个", 899.0], [6, 0, "小米万兆WiFi", "个", 1499.0], [6, 0, "万兆路由器", "个", 1499.0], [6, 1, "千兆交换机", "个", 129.0], [6, 1, "小米千兆8口交换机", "个", 129.0], [6, 1, "万兆交换机", "个", 349.0], [6, 1, "小米万兆交换机", "个", 349.0], [6, 1, "8口千兆AC交换机", "个", 1050.0], [6, 1, "AC+poe交换机(锐捷10口110w)", "个", 1100.0], [6, 1, "AC一体交换机", "个", 1600.0], [6, 2, "壁挂ap盖板", "个", 10.0], [6, 2, "壁挂ap", "个", 350.0], [6, 2, "WiFi(AX6000mesh组网)", "个", 439.0], [6, 2, "WIFImesh组网BE6500", "个", 449.0], [6, 2, "mesh组网", "个", 499.0], [6, 2, "吸顶ap", "个", 550.0], [6, 2, "WIFImesh组网(主路由)弱电箱", "个", 659.0], [7, 0, "200w低压电源", "米", 279.0], [7, 1, "弱电箱排插头", "个", 26.78], [7, 1, "四位排插", "个", 45.0], [7, 1, "485协议模块", "个", 480.0]], "alias": {"4|0|4寸场景屏": ["4寸中控场景屏", "4寸场景屏", "4寸家庭场景屏", "4寸智能场景屏", "4寸智能家庭场景屏", "4寸智能屏", "智能4寸场景屏", "智能4寸家庭场景屏"], "4|0|家庭屏上墙支架": ["中控屏上墙充电支架", "家庭屏上墙支架"], "4|0|4英寸场景屏": ["4英寸场景屏", "四寸智能场景屏", "四寸智能场景屏(3开关8场景)", "智能家庭场景屏"], "4|0|智能家庭中控屏": ["可移动家庭中控屏", "智能中控屏(门处)", "智能家庭中控屏", "过道中控屏"], "0|7|绿米Z1(零火三键)": ["绿米Z1(单零火通用三键)", "绿米Z1(零火三键)"], "0|7|绿米Z1(零火双键)": ["绿米Z1(单零火通用双键)", "绿米Z1(零火双键)"], "0|7|绿米Z1(零火单键)": ["绿米Z1(单零火通用单键)", "绿米Z1(零火单键)"], "0|1|开关刻字": ["开关刻字", "开关按键刻字", "开关按键刻字+图标", "开关按键刻字及图标"], "1|4|64G内存卡": ["64G内存卡", "场景按钮"], "1|4|玻璃胶": ["弯弧费", "物流费", "玻璃胶"], "2|5|轨道超过3米按80/米增加": ["5孔带Type-C与USB接口", "轨道超过3米按80/米增加", "零线发生器"], "2|5|梦幻帘轨道": ["布帘增加米数", "梦幻帘轨道", "纱帘增加米数"], "2|5|日夜帘=1.139*1.775": ["日夜帘=1.139*1.775", "日夜帘=2.141*1.743"], "4|0|智能安防": ["小爱智能音响Pro", "小爱音响pro(红外版)", "智能安防", "电动球阀", "监控"], "1|4|中枢无关": ["中枢无关", "平开窗推窗器", "斗胆灯", "灯光调试费"], "2|5|香格里拉帘电机": ["智能梦幻帘电机", "电动推窗器包安装0.48*1.158", "电动推窗器包安装0.558*1.158", "苹果HomePodmini", "西餐梦幻帘电机(未含轨道)", "香格里拉帘电机"], "4|0|中央集控屏": ["上墙语音总控屏", "中央集控屏", "中控家庭语音屏", "智能中控语音屏"], "0|9|无线开关三键白色": ["无线开关三键白色", "无线开关(三键)"], "0|9|无线开关双键白色": ["无线开关双键白色", "无线开关(白色双键)"], "0|9|无线开关(单键)": ["无线开关(单键)", "无线智能开关(单键)"], "0|6|平头熊X6三键开关": ["X6智能开关(三键)", "平头熊X6三键开关"], "0|6|平头熊X6双键开关": ["X6智能开关(双键)", "平头熊X6双键开关"], "0|6|平头熊X6单键开关": ["X6智能开关(单键)", "平头熊X6单键开关"], "0|3|AG玻璃(零火四键)": ["AG智能开关(四键零火版)", "AG智能开关(四键)(零火版)", "AG玻璃(零火四键)"], "0|3|AG玻璃(零火三键)": ["AG智能开关(三键零火版)", "AG智能开关(三键)(零火版)", "AG玻璃(零火三键)"], "0|3|AG智能开关(双键零火版)": ["AG智能开关(双键零火版)", "AG智能开关(双键)(零火版)"], "0|3|AG玻璃(零火单键)": ["AG智能开关(单键零火版)", "AG智能开关(单键)(零火版)", "AG玻璃(零火单键)"], "0|4|平头熊AE(单火三键)": ["AE智能开关(三键)(单火版)", "平头熊AE(单火三键)"], "0|4|平头熊AE(单火双键)": ["AE智能开关(双键)(单火版)", "平头熊AE(单火双键)"], "0|4|平头熊AE(单火单键)": ["AE智能开关(单键)(单火版)", "平头熊AE(单火单键)"], "0|10|智能插座": ["智能插座", "智能移动插座"], "5|0|16A挂机控制器": ["16A挂机控制器", "挂机控制器", "挂机空调伴侣", "挂机空调控制器", "挂机空调智能插座", "柜机控制器", "柜机红外控制", "空调伴侣", "空调控制器"], "5|0|柜机空调控制器": ["柜机空调控制器", "风管机控制器安装调试费"], "5|0|风管机控制": ["风管机控制", "风管机控制器", "风管机控制面板"], "5|0|中央空调控制器": ["中央空调vrf", "中央空调控制器"], "5|0|中央空调vfr": ["中央空调vfr", "中央空调控制网关"], "0|1|肤感玻璃(零火四键)": ["玻璃肤感智能开关(四键)(零火版)", "玻璃超薄肤感智能开关(零火四键)", "肤感玻璃智能开关(零火四键)", "肤感玻璃(零火四键)", "肤感玻璃(零火四键)原数量3"], "0|1|肤感玻璃(单火三键)": ["玻璃肤感智能开关(三键)(单火版)", "玻璃超薄肤感智能开关(单火三键)", "肤感玻璃(单火三键)"], "0|1|肤感玻璃(零火三键)": ["玻璃肤感智能开关(三键)(零火版)", "玻璃超薄肤感智能开关带屏显免刻字(零火三键)", "玻璃超薄肤感智能开关(零火三键)", "肤感玻璃智能开关(零火三键)", "肤感玻璃(零火三键)", "肤感玻璃(零火三键)原数量2"], "0|1|肤感玻璃(单火双键)": ["玻璃肤感智能开关(双键)(单火版)", "玻璃超薄肤感智能开关(单火双键)", "肤感玻璃(单火双键)"], "0|1|肤感玻璃(零火双键)": ["玻璃肤感智能开关(双键)(零火版)", "玻璃超薄肤感智能开关(零火双键)", "肤感玻璃智能开关(零火双键)", "肤感玻璃(零火双键)", "肤感玻璃(零火双键)原数量5"], "0|1|肤感玻璃(单火单键)": ["玻璃肤感智能开关(单键)(单火版)", "玻璃超薄肤感智能开关(单火单键)", "肤感玻璃(单火单键)"], "0|1|肤感玻璃(零火单键)": ["玻璃超薄肤感智能开关(零火单键)", "肤感玻璃智能开关(零火单键)", "肤感玻璃(零火单键)", "肤感玻璃(零火单键)原数量4"], "0|2|PC肤感超薄(零火四键)": ["PC肤感智能开关(零火四键)", "PC肤感超薄(零火四键)", "PC超薄肤感智能开关(零火四键)"], "0|2|PC肤感超薄(零火三键)": ["PC肤感智能开关(零火三键)", "PC肤感超薄(零火三键)", "PC超薄肤感智能开关(零火三键)"], "0|2|PC肤感超薄(零火双键)": ["PC肤感智能开关(零火双键)", "PC肤感超薄(零火双键)", "PC超薄肤感智能开关(零火双键)"], "0|2|PC肤感超薄(零火单键)": ["PC肤感智能开关(零火单键)", "PC肤感超薄(零火单键)", "PC超薄肤感智能开关(零火单键)"], "0|10|肤感玻璃五孔插座": ["肤感玻璃七孔插座", "肤感玻璃五孔插座"], "3|2|霸王锁体": ["定制锁舌", "送霸王锁体", "霸王锁体"], "2|5|日夜帘电机(上下两层单电机)": ["房日夜帘电机(上下两层单电机)", "日夜帘电机(上下两层单电机)"], "2|5|静音电机": ["CM定制款静音电机", "静音电机"], "2|3|卷帘面料": ["功能房卷帘布料(长2850高2645)", "卷帘布料1.43*2.9", "卷帘布料1.45*1.93", "卷帘布料1.47*1.93", "卷帘布料(长2645高1730)", "卷帘面料", "小卷帘布料(长2855高2150)", "小卷帘布料(长985高2150)", "过道右房卷帘布料(长2545高1730)", "过道左房卷帘布料(长2215高1725)", "香格里拉帘布料"], "2|3|梦幻帘布料及轨道2.75高": ["卷帘布料", "梦幻帘布料及轨道2.75高"], "2|3|日夜帘布料1.8*1.7": ["日夜帘布料1.8*1.7", "日夜帘布料2.1*1.7", "日夜帘布料(2.2+1.8)*1.7", "(日夜帘布料)1.75*(1.71-0.44)", "(日夜帘布料)2.3*1.5"], "1|4|智能调光一体式电源驱动": ["智能调光一体式电源驱动", "智能调光一体式电源驱动(窗帘灯带)"], "1|4|60w低压电源": ["60w低压电源", "智能60w电源驱动调光控制器", "智能电源驱动控制器60w(", "智能电源驱动控制器60w(天花", "智能电源驱动控制器60w(床头灯带", "智能电源驱动控制器60w(灯带", "智能电源驱动控制器60w(窗帘灯带", "智能电源驱动控制器60w(窗户处", "智能调光灯带60w驱动器+电源器0%-100%亮度调节-2700k-6500k色温调节"], "2|2|铝百叶": ["铝百叶", "铝百叶窗帘(含配件及传动箱)2.245*1.5", "铝百叶窗帘(含配件及传动箱)2.525*1.5"], "2|5|电动窗帘(双轨卧室5)": ["电动窗帘(双轨卧室1)", "电动窗帘(双轨卧室2)", "电动窗帘(双轨卧室3)", "电动窗帘(双轨卧室5)", "电动窗帘(双轨卧室)"], "1|2|裸板双色温智能灯带(": ["裸板双色温智能灯带(", "裸板双色温智能灯带(天花", "裸板双色温智能灯带(床头灯带", "裸板双色温智能灯带(灯带", "裸板双色温智能灯带(窗帘灯带", "裸板双色温智能灯带(窗户处"], "1|2|智能双色温全光谱调光灯带19.5": ["智能双色温全光谱调光灯带19.5", "智能双色温全光谱调光灯带(窗帘灯带)"], "0|1|1.8(一块布单键)布帘": ["1.8(一块布单键)布帘", "(纱帘侧)2.09(一块布单键)布帘"], "0|1|2.38(一块布单键)纱帘": ["2.38(一块布单键)纱帘", "2.565(一块布单键)纱帘", "2.84(一块布单键)纱帘"], "2|0|窗帘电机C2(含3米轨道)(纱帘2.2米)": ["窗帘电机C2(含3米轨道)(布帘2.2米)", "窗帘电机C2(含3米轨道)(布帘3.15米)", "窗帘电机C2(含3米轨道)(布帘4.02米)", "窗帘电机C2(含3米轨道)(纱帘0.95米)", "窗帘电机C2(含3米轨道)(纱帘2.2米)", "窗帘电机C2(含3米轨道)(纱帘2.58米)", "窗帘电机C2(含3米轨道)(纱帘3.15米)", "窗帘电机C2(含3米轨道)(纱帘4.02米)"], "2|0|杜亚35卷帘电机": ["杜亚35卷帘电机", "杜亚卷帘电机"], "2|0|25卷帘电机": ["25卷帘电机", "35卷帘电机", "卷帘电机"], "2|5|窗帘(电机含3米轨道)": ["卧室窗帘(电机含3米轨道)", "窗帘(电机含3米轨道)"], "2|0|窗帘电机(含3米轨道)": ["窗帘电机(含3米轨道)", "窗帘电机(含3米轨道)2.98米", "窗帘电机(含3米轨道)3.5米"], "2|0|(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)": ["(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(储物室)(单直轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(卧室2女儿房)(双直轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(双直轨)"], "2|0|(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)": ["(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(卧室一)(双弯轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(客房)(双弯轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(双弯轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(品茶区)(双弯轨)"], "2|0|杜亚静音款mesh智能窗帘电机": ["杜亚智能窗帘电机mesh静音款", "杜亚静音款mesh智能窗帘电机", "杜亚静音款mesh隐藏智能窗帘电机"], "2|0|窗帘电机": ["智能窗帘电机", "电动窗帘电机", "窗帘电机"], "2|0|静音窗帘电机": ["定制款静音窗帘电机", "智能窗帘电机mesh静音款", "窗帘电机(定制静音款)(双轨)", "窗帘电机(静音款)", "静音款mesh窗帘电机(单轨)", "静音款智能窗帘电机", "静音款窗帘电机", "静音款窗帘电机(mesh本地执行版)", "静音窗帘电机", "静音窗帘电机(1单轨)", "静音窗帘电机(2单轨)", "静音窗帘电机(单轨)", "静音窗帘电机(双轨)"], "2|0|(二层)定制静音款电动窗帘电机(卧室3)(双直轨)": ["(一层)定制静音款电动窗帘电机(储物室)(单直轨)", "(一层)定制静音款电动窗帘电机(单直轨)", "(一层)定制静音款电动窗帘电机(卧室2女儿房)(双直轨)", "(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "(二层)定制静音款电动窗帘电机(双直轨)"], "2|0|静音款窗帘电机(双弯轨)": ["静音款窗帘电机(单弯轨)(布料不超30公斤1个电机)超过要分轨道分电机", "静音款窗帘电机(双弯轨)", "(一层)定制静音款电动窗帘电机(卧室一)(双弯轨)", "(一层)定制静音款电动窗帘电机(双弯轨)", "(一层)定制静音款电动窗帘电机(客房)(双弯轨)", "(二层)定制静音款电动窗帘电机(双弯轨)", "(二层)定制静音款电动窗帘电机(品茶区)(双弯轨)"], "2|1|电机静音导轨/米2.0厚": ["杜亚静音款2.0厚窗帘轨道", "杜亚静音款2.0厚窗帘轨道3.161", "杜亚静音款2.0厚窗帘轨道5.79+0.3", "杜亚静音款2.0厚窗帘轨道(卧室)3.09", "杜亚静音款2.0厚窗帘轨道(大女儿房)3.047", "杜亚静音款2.0厚窗帘轨道(小女儿房)2.267", "杜亚静音款2.0厚窗帘轨道(布帘)2.635", "杜亚静音款2.0厚窗帘轨道(纱帘)1.695+6.3", "杜亚静音款2.0厚窗帘轨道(纱帘)2.635", "杜亚静音款2.0厚窗帘轨道(纱帘)3.5+1.7", "杜亚静音款2.0厚窗帘轨道(纱帘)3.542", "杜亚静音款2.0厚窗帘轨道(超出一米)", "电机静音导轨/米2.0厚"], "2|1|杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4": ["杜亚静音款2.0厚隐藏窗帘轨道(布帘)2.4", "杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4"], "2|1|窗帘轨道(单轨单电机)": ["沙发侧窗帘轨道(单轨单电机)", "窗帘轨道(单轨单电机)", "窗帘轨道(单轨双电机)", "茶室窗帘轨道(单轨单电机)"], "2|1|窗帘轨道(双轨)": ["三楼右卧室窗帘轨道(双轨)", "左卧室窗帘轨道(双轨)", "电梯卧室窗帘轨道(双轨)", "窗帘轨道(双轨四电机)", "窗帘轨道(双轨)"], "1|4|智能灯带电源300w": ["智能灯带电源300w", "磁吸轨道智能电源300w"], "1|4|智能300w电源驱动调光控制器": ["智能300w电源驱动调光控制器", "磁吸轨道电源(暗装)300w"], "1|4|智能灯带电源驱动300w": ["一体式智能灯带调光电源驱动300w", "智能灯带电源驱动300w", "智能磁吸轨道电源300w(双磁吸轨道)", "灯带一体式电源驱动300w", "灯带变压器24V300W"], "1|3|磁吸轨道导电接头": ["磁吸轨道导电接头", "磁吸轨道直线导通连接"], "1|3|磁吸轨道": ["磁吸轨道", "磁吸轨道泛光灯"], "1|3|磁吸轨道格栅灯": ["磁吸轨道格栅灯", "磁吸轨道格栅灯30cm(支持2700k-6500k色温调节-0%-100%光度调节)"], "1|3|磁吸轨道灯(泛光灯)": ["磁吸轨道泛光灯60cm(支持2700k-6500k色温调节-0%-100%光度调节)", "磁吸轨道灯(泛光灯)"], "1|4|灯带一体式电源驱动200w": ["智能调光一体式电源驱动器200w2700-6500色温调节-0%-100%亮度调节", "灯带一体式电源驱动200w", "磁吸轨道电源(明装200w)", "磁吸轨道电源(暗装200w)"], "1|0|智能射灯": ["射灯", "智能射灯", "智能射灯(2700-6500可调色温1%-100%可调亮度)", "智能筒射灯", "筒射灯"], "1|0|磁吸轨道射灯": ["智能射灯(RA>95、防眩晕、抗蓝光)", "磁吸轨道吊射灯", "磁吸轨道射灯"], "1|3|嵌入式磁吸轨道": ["嵌入式带边磁吸轨道", "嵌入式磁吸轨道", "磁吸轨道(嵌入式)3米一条"], "2|1|窗帘轨道2.0(静音款)弯轨": ["窗帘轨道2.0(静音款)弯轨", "窗帘轨道2.0(静音款)弯轨短", "窗帘轨道2.0(静音款)弯轨长", "静音款窗帘轨道(单弯轨)", "静音窗帘轨道(含弯轨)", "(一层)定制静音款电动窗帘轨道(卧室一)(双弯轨)", "(一层)定制静音款电动窗帘轨道(双弯轨)", "(一层)定制静音款电动窗帘轨道(客房)(双弯轨)", "(二层)定制静音款电动窗帘轨道(双弯轨)", "(二层)定制静音款电动窗帘轨道(品茶区)(双弯轨)"], "2|1|窗帘轨道": ["电动窗帘轨道", "窗帘轨道", "窗帘轨道(增加0.5米/条)"], "2|1|2.0静音窗帘轨道": ["2.0静音电动窗帘轨道", "2.0静音电动窗帘轨道(超出一米)", "2.0静音窗帘轨道", "CM定制款静音轨道", "定制款静音窗帘轨道2.0", "杜亚静音款2.0后窗帘轨道", "杜亚静音款2.0后窗帘轨道(布帘)=2.448+1.018", "杜亚静音款2.0后窗帘轨道(瑜伽房)=2.174+1.55", "杜亚静音款2.0后窗帘轨道(纱帘)=2.448+1.018", "杜亚静音款2.0后窗帘轨道(纱帘)=3.24+1.15", "杜亚静音窗帘轨道", "杜亚静音窗帘轨道2.965-0.51-0.155", "杜亚静音窗帘轨道3.14-0.95", "静音款智能窗帘轨道", "静音款窗帘轨道", "静音轨道"], "2|1|窗帘轨道2.0(静音款)直轨": ["窗帘轨道2.0(静音款)直轨", "(一层)定制静音款电动窗帘轨道(储物室)(单直轨)", "(一层)定制静音款电动窗帘轨道(单直轨)", "(一层)定制静音款电动窗帘轨道(卧室2女儿房)(双直轨)", "(二层)定制静音款电动窗帘轨道(卧室3)(双直轨)", "(二层)定制静音款电动窗帘轨道(双直轨)"], "2|1|静音窗帘轨道(双轨)": ["窗帘轨道(定制静音款)(双轨)", "静音窗帘轨道(A双轨)", "静音窗帘轨道(双轨)", "静音窗帘轨道(双轨)(长3945宽1500高2705)", "静音窗帘轨道(右卧室双轨布帘)", "静音窗帘轨道(右卧室双轨纱帘)", "静音窗帘轨道(左卧室双轨布帘)", "静音窗帘轨道(左卧室双轨纱帘)"], "2|1|静音窗帘轨道(1单轨)": ["杜亚静音窗帘轨道(单轨)", "静音款电动窗帘轨道(单轨)", "静音窗帘轨道(1单轨)", "静音窗帘轨道(2单轨)", "静音窗帘轨道(单轨布帘)", "静音窗帘轨道(单轨纱帘)", "静音窗帘轨道(单轨)"], "1|3|明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)": ["明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)", "明装磁吸轨道(简餐吧台)(实际测量尺寸3.01米、离墙间隔20cm)"], "1|5|E14智能灯泡": ["E14智能灯泡", "E27智能灯泡"], "1|0|(满星思维/黑骑士)75mm智能调光射灯": ["(满星思维/黑骑士)75mm智能单控调光筒射灯", "(满星思维/黑骑士)75mm智能调光射灯"], "1|0|12w射灯": ["12w射灯", "12w智能射灯(2700k-6500k)"], "1|0|智能射灯(12w-RA98)": ["智能射灯(12w-RA97)", "智能射灯(12w-RA98)"], "1|0|智能射灯(36度)": ["智能射灯(36度)", "智能射灯(过道36度)"], "1|0|米家智能射灯": ["米家智能射灯", "米家智能射灯(门)"], "1|4|筒射灯驱动": ["电源", "筒射灯驱动"], "1|4|灯带一体式驱动电源150瓦": ["一体式驱动电源", "智能灯带驱动", "灯带一体式驱动电源150瓦", "灯带电源器", "灯带驱动", "灯带驱动电源", "驱动", "驱动电源通断器"], "1|4|灯带电源驱动": ["智能橱柜灯带电源", "灯带电源驱动"], "1|4|灯带一体式驱动电源300瓦": ["灯带一体式驱动电源300瓦", "灯带电源+驱动"], "1|4|1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)": ["1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)", "床底灯带套餐(感应器、铝材灯槽、灯带、电源、驱动器、固定配件)", "智能床底灯带套餐(含电源、驱动器、灯带、灯槽、感应器)", "智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)"], "1|2|灯带接驳配件": ["U型型材(天花灯带1)", "U型型材(洗帘灯带)", "U型型材(立面灯带)", "灯带接驳配件"], "1|2|柜体灯带": ["天花灯带", "柜体灯带", "铝型灯槽10*10mm(灯带)"], "1|2|智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)": ["COB双色温灯带(一楼)2*1", "COB双色温灯带(主人房)4*1", "COB双色温灯带(女儿房)4*1", "COB双色温灯带(楼梯灯带)5*1", "COB双色温灯带(楼梯间)2*1", "COB双色温灯带(环形顶)14*1", "智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)"], "1|2|智能COB调光灯带": ["智能COB调光灯带", "智能调光COB灯带", "智能调光COB灯带(天花灯带1)", "智能调光COB灯带(洗帘灯带)", "智能调光COB灯带(立面灯带)"], "1|2|双色温灯带10米": ["三雄极光双色温灯带", "双色温灯带", "双色温灯带10米", "双色温灯带(2700-6500可调色温1%-100%可调亮度)"], "1|4|柜体专用调光驱动(米家)150w": ["150w24v变压器(层板)两线单色温", "柜体专用调光驱动(米家)150w"], "1|4|150w低压电源": ["150w低压电源", "智能灯带电源驱动150w"], "1|4|100w低压电源": ["100w24v杜邦接口变压器", "100w低压电源", "mesh版智能灯带一体式电源驱动100w", "灯带变压器24V100W"], "1|4|智能灯带电源120w": ["智能灯带电源120w", "米家智能电源120w"], "1|4|色温灯带智能驱动120w": ["一体式智能灯带调光电源驱动120w", "一体式驱动电源(120w)", "智能120w电源驱动调光控制器", "智能调光一体式电源驱动器120w2700-6500色温调节-0%-100%亮度调节", "灯带一体式电源驱动120w", "灯带电源一体驱动(120w)", "米家智能调光驱动120w", "色温灯带智能驱动120w"], "1|5|电视红外语控器": ["电视柜灯带2.97米", "电视红外语控器"], "1|2|cob无极调光灯带": ["cob无极调光灯带", "cob无极调光灯带2.57*4", "cob无极调光灯带("], "1|6|灯槽": ["18.5*43.5双眼皮灯槽", "灯槽"], "1|1|易来智能筒灯C3(10W-55°)": ["易来智能筒灯C3(10W-55°)", "易来智能筒灯C3(10W-55°)(门至过道)"], "1|1|75开孔智能筒灯": ["75开孔智能筒灯", "75开孔智能防水筒灯"], "1|1|7w筒灯": ["7w-10w智能筒灯", "7w筒灯", "筒灯7w无极调光"], "1|1|智能筒灯": ["智能筒灯", "智能筒灯(2700-6500可调色温1%-100%可调亮度)"], "3|0|人体感应器": ["人体感应器", "人体感应器(纽扣电池版)"], "3|0|人体存在传感器": ["人体存在传感器", "人体存在感应器", "人体存在感应器(健身房入门处)", "人体存在感应器(免布线纽扣电池版)", "人体存在感应器(入门处)", "人体存在感应器(卧室过道)有线", "免布线人体存在感应器", "公卫人体存在感应器"], "3|0|人体存在感应器顶装": ["人体存在感应器顶装", "人体存在感应器(顶装75mm)", "人体存在感应器(顶装版)", "人体纯在感应器(顶装版)"], "3|0|领普人体感应器": ["领普人体感应器", "领普人体纯在感应器"], "3|0|嵌入式人体存在感应器": ["人体存在感应器(公卫)(嵌入式75mm开孔)", "人体存在感应器(嵌入式75mm开孔)", "嵌入式人体存在感应器"], "3|0|领普顶装人体存在传感器": ["领普人体纯在感应器(顶装版)", "领普顶装人体存在传感器"], "3|0|人体存在感应器无线": ["人体存在感应器无线", "人体存在感应器(公卫干湿区)无线", "人体存在感应器(干湿区)无线"], "3|0|小米人体传感器2S": ["小米人体传感器2S", "小米人体传感器2s", "小米人体感应器", "小米人体感应器2s(入门灯亮)"], "3|1|门磁感应器": ["温湿度传感器", "磁吸感应器", "门磁感应器"], "3|1|水浸传感器": ["水浸传感器", "门窗传感器"], "3|2|烟雾报警器": ["天然气报警器", "烟雾报警器", "燃气报警器"], "4|0|小米家庭屏pro8上墙支架": ["Xiaomi智能家庭屏Pro8上墙支架", "小米家庭屏pro8上墙支架"], "4|0|小米pro8智能屏": ["Xiaomi智能家庭屏Pro8", "小米pro8家庭屏", "小米pro8智能屏", "小米家庭屏pro8"], "3|1|小米智能摄像机云台版2k": ["小米智能摄像机云台版2k", "小米监控"], "3|1|小米智能摄像机2云台版": ["小米小爱音响pro", "小米智能摄像机2云台版"], "4|0|AK智能屏4英寸": ["AK场景屏", "AK智能场景屏", "AK智能屏", "AK智能屏4英寸", "AK智能屏(4英寸)", "ptxAK场景屏", "平头熊AK智能屏4英寸"], "4|0|4寸智能场景控制屏": ["4寸智能场景控制屏", "超薄4寸智能场景控制屏"], "5|0|风管机内置控制器": ["智能风管机内置控制器", "风管机内置控制器", "风管机内置控制模块", "风管机内置控制网关"], "5|0|风管机内置控制网关mesh": ["智能风管机内置控制器mesh", "风管机内置控制网关mesh"], "4|2|中枢网关": ["中枢网关", "智能网关", "网关"], "4|1|智能音箱": ["小爱智能音箱Pro", "小爱音箱Pro", "小爱音箱pro", "智能音箱"], "4|1|小米音箱pro": ["小米小爱音箱Pro", "小米智能音箱pro", "小米音箱pro"], "5|1|新风内置控制器": ["新风内置控制器", "新风内置控制模块"], "5|2|米家智能浴霸S1": ["小米智能浴霸S1", "米家智能浴霸S1"], "5|2|小米浴霸pro": ["小米浴霸Pro", "小米浴霸pro", "米家智能浴霸Pro", "米家智能浴霸pro"], "6|1|万兆交换机": ["万兆交换机", "交换机"], "6|1|8口千兆AC交换机": ["10口千兆AC交换机", "8口千兆AC交换机"], "6|1|小米千兆8口交换机": ["小米千兆8口交换机", "小米千兆交换机"], "1|4|小米be5000": ["小米BE5000", "小米be5000"], "6|2|mesh组网": ["WIFImesh组网", "mesh组网"], "6|0|小米万兆WiFi": ["WiFi(小米万兆路由器)", "小米万兆WiFi"], "6|0|WIFI8口交换机": ["WIFI8口交换机", "WIFI千兆交换机"], "6|0|WIFI-锐捷4口千兆交换机": ["WIFI-锐捷4口千兆交换机", "WIFI-锐捷8口千兆交换机"], "6|0|WIFI-ap162e": ["WIFI-ap162e", "WIFI万兆交换机", "WIFI交换机"], "6|0|WiFi": ["WIFI子路由", "WiFi", "WiFi子母路由"], "6|0|WIFI覆盖": ["WIFI主路由", "WIFI覆盖"], "6|0|WIFIBE6500": ["WIFIBE6500", "WIFI子路由BE6500"], "6|0|WiFi-AX6000": ["Redmi路由器AX6000", "WiFi-AX6000"], "6|0|米家路由器BE5000": ["WiFi(小米路由器BE5000)", "米家路由器BE5000"], "3|4|小米摄像头3云台版": ["小米摄像头3云台版", "小米摄像头3(云台版)", "小米智能摄像头", "小米智能摄像头2云台版"], "5|3|米家全能扫拖机器人M30S": ["小米全能扫拖机器人m30s", "米家全能扫拖机器人M30S"], "5|4|智能晾衣架": ["智能晾衣架", "电动晾衣架"], "5|4|米家智能晾衣架1s": ["米家智能晾衣架1s", "米家智能晾衣架pro"], "5|4|米家智能晾衣架Pro": ["电动晾衣架(小米1s智能晾衣架)", "米家智能晾衣架Pro"], "2|4|电动开窗器": ["太阳能开窗器", "电动开窗器", "电动开窗器(外推)(锂电池款含雨水感应器)", "电动开窗器(锂电池款含雨水感应器)"]}};
438
- /* ===== 自动升级检查(版本 1.1.0):远端版本更新则跳转最新版,任何失败静默忽略 ===== */
439
- try { (function () {
440
- var MYVER = '1.1.0';
441
- function _cmp(a, b) { try { var x = String(a).split('.'), y = String(b).split('.'), n = Math.max(x.length, y.length); for (var i = 0; i < n; i++) { var p = parseInt(x[i] || '0', 10) || 0, q = parseInt(y[i] || '0', 10) || 0; if (p > q) return 1; if (p < q) return -1; } return 0; } catch (e) { return 0; } }
442
- function _go(v) { try { location.replace('https://unpkg.com/shulan-quote@' + v + '/index.html'); } catch (e) { } }
443
- try {
444
- if (typeof fetch !== 'function') return;
445
- fetch('https://unpkg.com/shulan-quote/package.json?t=' + Date.now(), { cache: 'no-store' }).then(function (r) { return r && r.ok ? r.json() : null; }).then(function (j) { try { if (!j || !j.version) return; var rv = String(j.version); if (rv !== MYVER && _cmp(rv, MYVER) > 0) _go(rv); } catch (e) { } }).catch(function () { });
446
- } catch (e) { }
447
- })(); } catch (e) { }
448
- /* ================= 树懒智汇家报价系统 v7 ================= */
449
- var RAW = DATA;
450
- var REV = 'slzj-v7-480';
451
- var K_DB = 'slzj_db_v7', K_SESS = 'slzj_sess_v7', K_HIST = 'slzj_hist_v7';
452
- var K_CLOUD = 'slzj_cloud_v1', K_DBTS = 'slzj_dbts_v1';
453
- /* 内置云端配置:填好后,任何人打开本页面都会自动连上同一份产品库,无需手动设置 */
454
- var CLOUD_DEFAULT = { url: '', key: '', room: '' };
455
-
456
- /* ---------- 基础工具 ---------- */
457
- function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]; }); }
458
- function money(n){ n = Math.round((+n||0)*100)/100; return '¥' + n.toLocaleString('zh-CN',{minimumFractionDigits:0,maximumFractionDigits:2}); }
459
- function p2(n){ return n<10 ? '0'+n : ''+n; }
460
- function stamp(){ var d=new Date(); return d.getFullYear()+p2(d.getMonth()+1)+p2(d.getDate()); }
461
- function toast(msg){ var t=document.getElementById('toast'); t.textContent=msg; t.classList.add('on'); clearTimeout(t._h); t._h=setTimeout(function(){ t.classList.remove('on'); }, 1900); }
462
- function $(id){ return document.getElementById(id); }
463
- function clone(o){ return JSON.parse(JSON.stringify(o)); }
464
-
465
- /* ---------- 数据层 ---------- */
466
- function aliasIndex(){
467
- var m = {}, a = RAW.alias || {}, k, i1, i2;
468
- for(k in a){
469
- i1 = k.indexOf('|'); i2 = k.indexOf('|', i1+1);
470
- if(i1 < 0 || i2 < 0) continue;
471
- m[(+k.slice(0,i1)) + '|' + (+k.slice(i1+1,i2)) + '|' + k.slice(i2+1)] = a[k];
472
- }
473
- return m;
474
- }
475
- function buildBase(){
476
- var am = aliasIndex(), items = [], cats = [], i, it;
477
- for(i=0; i<RAW.items.length; i++){
478
- it = RAW.items[i];
479
- items.push({
480
- id: 'b'+i, ci: it[0], si: it[1], name: it[2], unit: it[3], price: it[4],
481
- alias: am[it[0]+'|'+it[1]+'|'+it[2]] || []
482
- });
483
- }
484
- for(i=0; i<RAW.cats.length; i++) cats.push({ n: RAW.cats[i].n, subs: RAW.cats[i].subs.slice() });
485
- return { rev: REV, cats: cats, items: items, seq: 0 };
486
- }
487
- var DB = (function(){
488
- try{
489
- var raw = localStorage.getItem(K_DB);
490
- if(raw){
491
- var d = JSON.parse(raw);
492
- if(d && d.rev === REV && d.cats && d.cats.length && d.items && d.items.length) return d;
493
- }
494
- }catch(e){}
495
- return buildBase();
496
- })();
497
- function saveDB(){
498
- try{
499
- localStorage.setItem(K_DB, JSON.stringify(DB));
500
- localStorage.setItem(K_DBTS, String(Date.now()));
501
- }
502
- catch(e){ toast('本机保存失败:' + e.message); }
503
- if(!cloudSkip) cloudPush();
504
- }
505
-
506
- /* ---------- 排序:单键→双键→三键→四键 …… ---------- */
507
- var CN = {'一':1,'二':2,'两':2,'双':2,'单':1,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10};
508
- function keyNum(n){
509
- var m = n.match(/([一二两双单三四五六七八九十])\s*键/);
510
- if(m) return CN[m[1]] || 99;
511
- m = n.match(/([1-9]|1[0-2])\s*键/);
512
- if(m) return +m[1];
513
- return 99;
514
- }
515
- function fireKind(n){
516
- if(/零火/.test(n)) return 2;
517
- if(/单火/.test(n)) return 1;
518
- return 0;
519
- }
520
- function sortKey(a, b){
521
- if(a.si !== b.si) return a.si - b.si;
522
- var ka = keyNum(a.name), kb = keyNum(b.name);
523
- if(ka !== kb) return ka - kb;
524
- var fa = fireKind(a.name), fb = fireKind(b.name);
525
- if(fa !== fb) return fa - fb;
526
- if(a.price !== b.price) return a.price - b.price;
527
- return String(a.name).localeCompare(String(b.name), 'zh-CN');
528
- }
529
-
530
- /* ---------- 状态 ---------- */
531
- var state = { ci:0, si:-1, kw:'', bill:[], fee:25, editing:null };
532
- function saveSess(){
533
- try{ localStorage.setItem(K_SESS, JSON.stringify({bill:state.bill, fee:state.fee, editing:state.editing})); }catch(e){}
534
- }
535
- function loadSess(){
536
- try{
537
- var d = JSON.parse(localStorage.getItem(K_SESS) || '{}');
538
- if(d.bill && d.bill.length) state.bill = d.bill;
539
- if(typeof d.fee === 'number') state.fee = d.fee;
540
- if(d.editing && d.editing.id) state.editing = d.editing;
541
- }catch(e){}
542
- /* 兼容旧版明细结构 */
543
- var out = [];
544
- state.bill.forEach(function(b){
545
- if(b && b.name && b.unit !== undefined){ out.push(b); }
546
- });
547
- state.bill = out;
548
- }
549
-
550
- /* ---------- 渲染 ---------- */
551
- function itemsIn(ci, si){
552
- var r = [];
553
- DB.items.forEach(function(x){ if(x.ci === ci && (si < 0 || x.si === si)) r.push(x); });
554
- return r;
555
- }
556
- function renderNav(){
557
- var h = '<div class="nav-t">板块</div>', cnt;
558
- DB.cats.forEach(function(c, i){
559
- cnt = 0;
560
- DB.items.forEach(function(x){ if(x.ci === i) cnt++; });
561
- h += '<div class="navi' + (state.ci === i ? ' on' : '') + '" data-ci="' + i + '">'
562
- + '<span class="idx">' + (i+1) + '</span><span class="n">' + esc(c.n) + '</span><span class="c">' + cnt + '</span></div>';
563
- });
564
- $('nav').innerHTML = h;
565
- }
566
- function renderStage(){
567
- if(!DB.cats.length){ $('catName').textContent = '暂无类目'; $('catMeta').textContent = ''; $('chips').innerHTML = ''; $('plist').innerHTML = '<div class="empty">产品库为空,点右上角「产品库」新增类目和产品</div>'; return; }
568
- if(state.ci >= DB.cats.length) state.ci = 0;
569
- var cat = DB.cats[state.ci], subs = {}, total = 0, j;
570
- DB.items.forEach(function(x){ if(x.ci === state.ci){ subs[x.si] = (subs[x.si]||0) + 1; total++; } });
571
- $('catName').textContent = cat.n;
572
- $('catMeta').textContent = total + ' 款产品 · ' + cat.subs.length + ' 个子类';
573
- var ch = '<div class="chip' + (state.si < 0 ? ' on' : '') + '" data-si="-1">全部<span class="cn">' + total + '</span></div>';
574
- cat.subs.forEach(function(s, k){
575
- if(!subs[k]) return;
576
- ch += '<div class="chip' + (state.si === k ? ' on' : '') + '" data-si="' + k + '">' + esc(s) + '<span class="cn">' + subs[k] + '</span></div>';
577
- });
578
- $('chips').innerHTML = ch;
579
- renderList();
580
- }
581
- function renderList(){
582
- var kw = String(state.kw||'').trim().toLowerCase(), list = [], html = '', i, x, ok, al, k;
583
- if(kw){
584
- for(i=0; i<DB.items.length; i++){
585
- x = DB.items[i];
586
- ok = String(x.name).toLowerCase().indexOf(kw) >= 0;
587
- if(!ok && x.alias){ for(k=0; k<x.alias.length; k++){ if(String(x.alias[k]).toLowerCase().indexOf(kw) >= 0){ ok = true; break; } } }
588
- if(ok) list.push(x);
589
- }
590
- }else{
591
- list = itemsIn(state.ci, state.si);
592
- }
593
- list.sort(sortKey);
594
- if(!list.length){
595
- html = '<div class="empty">没有匹配的产品,换个关键词试试<br>或到「产品库」里新增</div>';
596
- }else{
597
- var added = {};
598
- state.bill.forEach(function(b){ added[b.pid] = 1; });
599
- for(i=0; i<list.length; i++){
600
- x = list[i];
601
- al = x.alias && x.alias.length ? x.alias : null;
602
- html += '<div class="pcard' + (added[x.id] ? ' added' : '') + '" data-id="' + x.id + '"'
603
- + (al ? ' title="已合并叫法:' + esc(al.join(' / ')) + '"' : '') + '>'
604
- + '<button class="pdel" data-pdel="' + x.id + '" title="从产品库删除">&times;</button>'
605
- + (al ? '<span class="alias">合' + al.length + '</span>' : '')
606
- + '<div class="pn">' + esc(x.name) + '</div>'
607
- + '<div class="pm"><div class="pp">' + money(x.price) + '<small>/' + esc(x.unit) + '</small></div>'
608
- + '<div class="add">+</div></div></div>';
609
- }
610
- }
611
- $('plist').innerHTML = html;
612
- }
613
- function renderBill(){
614
- var body = $('billbody');
615
- if(!state.bill.length){
616
- body.innerHTML = '<div class="bempty">左侧选板块,点产品卡加入报价<br>数量和单价都可以再改</div>';
617
- }else{
618
- var groups = {}, order = [], h = '';
619
- state.bill.forEach(function(b, i){
620
- var key = b.ci + '_' + b.si;
621
- if(!groups[key]){ groups[key] = {cat:b.cat, sub:b.sub, rows:[]}; order.push(key); }
622
- groups[key].rows.push({b:b, i:i});
623
- });
624
- order.forEach(function(key){
625
- var g = groups[key], sum = 0;
626
- g.rows.forEach(function(r){ sum += r.b.qty * r.b.price; });
627
- h += '<div class="bgrp" data-g="' + key + '"><div class="bgrp-h"><i></i>'
628
- + esc(g.cat) + (g.sub ? ' · ' + esc(g.sub) : '')
629
- + '<em class="gsum">' + money(sum) + '</em></div>';
630
- g.rows.forEach(function(r){
631
- h += '<div class="row" data-i="' + r.i + '">'
632
- + '<button class="delbtn" data-del="' + r.i + '">&times;</button>'
633
- + '<div class="rn">' + esc(r.b.name) + '</div>'
634
- + '<div class="calc">'
635
- + '<span class="x">¥</span><input class="ip" type="number" min="0" step="1" value="' + r.b.price + '" data-price="' + r.i + '">'
636
- + '<span class="x">&times;</span><input class="iq" type="number" min="0" step="1" value="' + r.b.qty + '" data-qty="' + r.i + '">'
637
- + '<span class="x">' + esc(r.b.unit) + '</span>'
638
- + '<span class="amt">' + money(r.b.qty * r.b.price) + '</span>'
639
- + '</div></div>';
640
- });
641
- h += '</div>';
642
- });
643
- body.innerHTML = h;
644
- }
645
- updateSums();
646
- saveSess();
647
- }
648
- function updateSums(){
649
- var sub = 0, qty = 0;
650
- state.bill.forEach(function(b, i){
651
- var s = b.qty * b.price; sub += s; qty += b.qty;
652
- var a = document.querySelector('.row[data-i="' + i + '"] .amt');
653
- if(a) a.textContent = money(s);
654
- });
655
- document.querySelectorAll('.bgrp').forEach(function(el){
656
- var key = el.dataset.g, sum = 0;
657
- state.bill.forEach(function(b){ if(b.ci + '_' + b.si === key) sum += b.qty * b.price; });
658
- var em = el.querySelector('.gsum');
659
- if(em) em.textContent = money(sum);
660
- });
661
- var feeAmt = sub * state.fee / 100;
662
- $('subAmt').textContent = money(sub);
663
- $('feeAmt').textContent = money(feeAmt);
664
- $('totAmt').textContent = money(sub + feeAmt);
665
- $('feeTag').textContent = state.fee + '%';
666
- $('qtyTag').textContent = qty ? ('共 ' + qty + ' 件') : '';
667
- $('cnt').textContent = state.bill.length ? (state.bill.length + ' 条 / ' + qty + ' 件') : '';
668
- }
669
- function renderEditBar(){
670
- var bar = $('editbar'), eb = $('ebSave'), en = $('ebNew'), ex = $('ebExit');
671
- if(state.editing && state.editing.id){
672
- bar.classList.add('on');
673
- var nm = document.createElement('span');
674
- $('ebText').textContent = '正在编辑:' + state.editing.name;
675
- eb.style.display = ''; en.style.display = ''; ex.style.display = '';
676
- }else{
677
- bar.classList.remove('on');
678
- }
679
- }
680
- function renderAll(){
681
- renderNav(); renderStage(); renderBill(); renderEditBar();
682
- $('stat').textContent = ' · ' + DB.items.length + ' 款产品 · ' + DB.cats.length + ' 个类目';
683
- }
684
-
685
- /* ---------- 加购 ---------- */
686
- function addProduct(it){
687
- var line = null;
688
- state.bill.forEach(function(b){ if(b.pid === it.id) line = b; });
689
- if(line){ line.qty += 1; }
690
- else{
691
- state.bill.push({
692
- pid: it.id, ci: it.ci, si: it.si,
693
- cat: DB.cats[it.ci] ? DB.cats[it.ci].n : '',
694
- sub: (DB.cats[it.ci] && DB.cats[it.ci].subs[it.si]) || '',
695
- name: it.name, unit: it.unit, price: it.price, qty: 1
696
- });
697
- }
698
- renderBill();
699
- var card = document.querySelector('.pcard[data-id="' + it.id + '"]');
700
- if(card) card.classList.add('added');
701
- }
702
-
703
- /* ---------- 历史报价 ---------- */
704
- function histAll(){
705
- try{
706
- var a = JSON.parse(localStorage.getItem(K_HIST) || '[]');
707
- return Object.prototype.toString.call(a) === '[object Array]' ? a : [];
708
- }catch(e){ return []; }
709
- }
710
- function histPut(list){
711
- try{ localStorage.setItem(K_HIST, JSON.stringify(list)); }
712
- catch(e){ toast('历史保存失败:' + e.message); }
713
- }
714
- function quoteTotal(rows, fee){
715
- var sub = 0;
716
- (rows||[]).forEach(function(r){ sub += r.qty * r.price; });
717
- return sub * (1 + (fee||0)/100);
718
- }
719
- function renderHist(){
720
- var list = histAll(), box = $('histBody'), h = '';
721
- if(!list.length){
722
- box.innerHTML = '<div class="hempty">还没有保存过报价单<br>在右侧明细里点「保存报价单」即可存档,随时载入修改</div>';
723
- return;
724
- }
725
- list.forEach(function(q){
726
- h += '<div class="hitem">'
727
- + '<div class="hi"><div class="hn">' + esc(q.name) + '</div>'
728
- + '<div class="hm">' + esc(q.date) + ' · ' + (q.rows ? q.rows.length : 0) + ' 条 · 服务费 ' + (q.fee||0) + '%</div></div>'
729
- + '<div class="hs">' + money(quoteTotal(q.rows, q.fee)) + '</div>'
730
- + '<div class="ha">'
731
- + '<button class="mini primary" data-open="' + q.id + '">载入</button>'
732
- + '<button class="mini" data-rename="' + q.id + '">改名</button>'
733
- + '<button class="mini danger" data-delq="' + q.id + '">删除</button>'
734
- + '</div></div>';
735
- });
736
- box.innerHTML = h;
737
- }
738
- function saveQuote(asNew){
739
- if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
740
- var list = histAll(), rec = null, i;
741
- if(state.editing && !asNew){
742
- for(i=0; i<list.length; i++) if(list[i].id === state.editing.id) rec = list[i];
743
- }
744
- var nm;
745
- if(rec){ nm = rec.name; }
746
- else{
747
- var def = state.editing ? (state.editing.name + ' 副本') : ('报价单 ' + stamp());
748
- var v = prompt('报价单名称(如:张先生-三房两厅)', def);
749
- if(v === null) return;
750
- nm = String(v || '').trim() || def;
751
- }
752
- var data = {
753
- id: rec ? rec.id : ('q' + Date.now()),
754
- name: nm,
755
- date: new Date().toLocaleString('zh-CN'),
756
- fee: state.fee,
757
- rows: clone(state.bill),
758
- updated: Date.now()
759
- };
760
- if(rec){
761
- for(i=0; i<list.length; i++) if(list[i].id === rec.id) list[i] = data;
762
- }else{
763
- list.unshift(data);
764
- }
765
- histPut(list);
766
- state.editing = { id: data.id, name: data.name };
767
- renderEditBar();
768
- saveSess();
769
- renderHist();
770
- toast(rec ? ('已更新:' + nm) : ('已保存:' + nm));
771
- }
772
- function openQuote(id){
773
- var list = histAll(), rec = null;
774
- list.forEach(function(q){ if(q.id === id) rec = q; });
775
- if(!rec) return;
776
- if(state.bill.length && !confirm('当前明细会被「' + rec.name + '」覆盖,继续?')) return;
777
- state.bill = clone(rec.rows || []);
778
- state.fee = rec.fee || 0;
779
- state.editing = { id: rec.id, name: rec.name };
780
- $('fee').value = state.fee;
781
- if(state.bill.length && DB.cats[state.bill[0].ci]){ state.ci = state.bill[0].ci; state.si = -1; }
782
- renderAll();
783
- closeModal('mHist');
784
- toast('已载入:' + rec.name);
785
- }
786
-
787
- /* ---------- 产品库管理 ---------- */
788
- var mgr = { ci: 0, si: -1, kw: '' };
789
- function openMgr(){
790
- mgr.ci = state.ci; mgr.si = state.si >= 0 ? state.si : 0; mgr.kw = '';
791
- $('mgrKw').value = '';
792
- $('addRow').style.display = 'none';
793
- renderMgr();
794
- show('mMgr');
795
- }
796
- function renderMgr(){
797
- if(mgr.ci >= DB.cats.length) mgr.ci = 0;
798
- var cat = DB.cats[mgr.ci];
799
- if(cat && (mgr.si >= cat.subs.length)) mgr.si = cat.subs.length ? 0 : -1;
800
- renderCatList(); renderSubList(); renderItemRows();
801
- $('mgrTip').textContent = '共 ' + DB.items.length + ' 款产品 / ' + DB.cats.length + ' 个类目 · 改动自动保存';
802
- }
803
- function renderCatList(){
804
- var h = '';
805
- DB.cats.forEach(function(c, i){
806
- var n = 0;
807
- DB.items.forEach(function(x){ if(x.ci === i) n++; });
808
- h += '<div class="mli' + (i === mgr.ci ? ' on' : '') + '" data-ci="' + i + '">'
809
- + '<span class="nm">' + esc(c.n) + '</span><span class="num">' + n + '</span>'
810
- + '<button class="x" data-rencat="' + i + '" title="重命名">✎</button>'
811
- + '<button class="x" data-delcat="' + i + '" title="删除类目">&times;</button></div>';
812
- });
813
- if(!DB.cats.length) h = '<div class="hempty" style="padding:20px 8px">暂无类目</div>';
814
- $('catList').innerHTML = h;
815
- }
816
- function renderSubList(){
817
- var cat = DB.cats[mgr.ci], h = '';
818
- $('subTitle').textContent = '子类' + (cat ? ':' + cat.n : '');
819
- if(cat){
820
- cat.subs.forEach(function(s, j){
821
- var n = 0;
822
- DB.items.forEach(function(x){ if(x.ci === mgr.ci && x.si === j) n++; });
823
- h += '<div class="mli' + (j === mgr.si ? ' on' : '') + '" data-si="' + j + '">'
824
- + '<span class="nm">' + esc(s) + '</span><span class="num">' + n + '</span>'
825
- + '<button class="x" data-rensub="' + j + '" title="重命名">✎</button>'
826
- + '<button class="x" data-delsub="' + j + '" title="删除子类">&times;</button></div>';
827
- });
828
- }
829
- if(!h) h = '<div class="hempty" style="padding:20px 8px">暂无子类</div>';
830
- $('subList').innerHTML = h;
831
- }
832
- function renderItemRows(){
833
- var kw = mgr.kw.trim().toLowerCase(), list = [], h = '';
834
- DB.items.forEach(function(x){
835
- if(x.ci !== mgr.ci) return;
836
- if(mgr.si >= 0 && x.si !== mgr.si) return;
837
- if(kw && String(x.name).toLowerCase().indexOf(kw) < 0) return;
838
- list.push(x);
839
- });
840
- list.sort(sortKey);
841
- $('itemTitle').textContent = '产品' + (list.length ? '(' + list.length + ')' : '');
842
- list.slice(0, 500).forEach(function(x){
843
- h += '<div class="mtr" data-id="' + x.id + '">'
844
- + '<input class="nm" value="' + esc(x.name) + '" data-f="name">'
845
- + '<input class="un" value="' + esc(x.unit) + '" data-f="unit">'
846
- + '<input class="pr" type="number" min="0" step="1" value="' + x.price + '" data-f="price">'
847
- + '<button class="x" data-delitem="' + x.id + '" title="删除产品">&times;</button>'
848
- + '</div>';
849
- });
850
- if(!h) h = '<div class="hempty" style="padding:24px 8px">这里还没有产品,点「+ 新增产品」添加</div>';
851
- $('itemRows').innerHTML = h;
852
- }
853
- function delCat(i){
854
- var cat = DB.cats[i], cnt = 0;
855
- if(!cat) return;
856
- DB.items.forEach(function(x){ if(x.ci === i) cnt++; });
857
- if(!confirm('删除类目「' + cat.n + '」?\n其中 ' + cnt + ' 款产品会一并删除,且不可恢复。')) return;
858
- DB.cats.splice(i, 1);
859
- DB.items = DB.items.filter(function(x){ return x.ci !== i; });
860
- DB.items.forEach(function(x){ if(x.ci > i) x.ci--; });
861
- if(mgr.ci >= DB.cats.length) mgr.ci = Math.max(0, DB.cats.length - 1);
862
- mgr.si = 0;
863
- if(state.ci >= DB.cats.length) state.ci = 0;
864
- saveDB(); renderMgr(); renderAll();
865
- toast('已删除类目');
866
- }
867
- function delSub(i, j){
868
- var cat = DB.cats[i];
869
- if(!cat) return;
870
- var cnt = 0;
871
- DB.items.forEach(function(x){ if(x.ci === i && x.si === j) cnt++; });
872
- if(!confirm('删除子类「' + cat.subs[j] + '」?\n其中 ' + cnt + ' 款产品会一并删除,且不可恢复。')) return;
873
- cat.subs.splice(j, 1);
874
- DB.items = DB.items.filter(function(x){ return !(x.ci === i && x.si === j); });
875
- DB.items.forEach(function(x){ if(x.ci === i && x.si > j) x.si--; });
876
- if(mgr.si >= cat.subs.length) mgr.si = Math.max(0, cat.subs.length - 1);
877
- saveDB(); renderMgr(); renderAll();
878
- toast('已删除子类');
879
- }
880
-
881
- /* ---------- 导出 ---------- */
882
- var isWX = /MicroMessenger/i.test(navigator.userAgent);
883
- function safeName(s){ return String(s || '报价单').replace(/[\\/:*?"<>|\r\n]/g, '_').slice(0, 60); }
884
- function dl(blob, filename){
885
- var ok = false;
886
- try{
887
- if(navigator.msSaveOrOpenBlob){ navigator.msSaveOrOpenBlob(blob, filename); ok = true; }
888
- }catch(e){}
889
- if(!ok){
890
- var url = URL.createObjectURL(blob);
891
- var a = document.createElement('a');
892
- a.href = url;
893
- a.download = filename;
894
- a.rel = 'noopener';
895
- a.style.cssText = 'position:fixed;left:-9999px;top:0;opacity:0';
896
- document.body.appendChild(a);
897
- try{
898
- if(typeof a.click === 'function'){ a.click(); }
899
- else{
900
- var ev = document.createEvent('MouseEvents');
901
- ev.initEvent('click', true, true);
902
- a.dispatchEvent(ev);
903
- }
904
- ok = true;
905
- }catch(e){ ok = false; }
906
- setTimeout(function(){
907
- try{ document.body.removeChild(a); }catch(e){}
908
- try{ URL.revokeObjectURL(url); }catch(e){}
909
- }, 2500);
910
- }
911
- return ok;
912
- }
913
- function quoteTitle(){
914
- return state.editing && state.editing.name ? state.editing.name : ('智能家居报价单_' + stamp());
915
- }
916
- function billLines(){
917
- var rows = state.bill, out = [], lastKey = '', sub = 0, i, b, key;
918
- for(i=0; i<rows.length; i++){
919
- b = rows[i]; key = b.ci + '_' + b.si;
920
- if(key !== lastKey){ out.push({ type:'grp', text: b.cat + (b.sub ? ' · ' + b.sub : '') }); lastKey = key; }
921
- out.push({ type:'row', b:b });
922
- sub += b.qty * b.price;
923
- }
924
- return { lines: out, sub: sub };
925
- }
926
- function exportWord(){
927
- if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
928
- var data = billLines(), sub = data.sub, feeAmt = sub * state.fee / 100;
929
- var title = quoteTitle();
930
- var d = new Date(), ds = d.getFullYear() + '-' + p2(d.getMonth()+1) + '-' + p2(d.getDate());
931
- var h = [];
932
- h.push('<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="utf-8"><title>' + esc(title) + '</title>');
933
- h.push('<style>@page{size:21cm 29.7cm;margin:1.6cm 1.5cm}body{font-family:"宋体",SimSun,serif;font-size:10.5pt;color:#000}');
934
- h.push('h1{font-family:"微软雅黑";font-size:17pt;text-align:center;margin:0 0 4pt;letter-spacing:2pt}');
935
- h.push('.sub{text-align:center;font-size:9pt;color:#555;margin-bottom:12pt}');
936
- h.push('table{border-collapse:collapse;width:100%;margin-bottom:10pt}');
937
- h.push('th,td{border:1px solid #444;padding:4pt 5pt;font-size:9.5pt;vertical-align:middle}');
938
- h.push('th{background:#e9eff5;font-weight:bold;text-align:center}');
939
- h.push('.c{text-align:center}.r{text-align:right}');
940
- h.push('.grp td{background:#f4f7fa;font-weight:bold}');
941
- h.push('.sub2 td{background:#f7fbf9;font-weight:bold}');
942
- h.push('.tot td{background:#eef7f3;font-weight:bold;font-size:10.5pt}');
943
- h.push('.note{font-size:9pt;color:#444;line-height:1.9}');
944
- h.push('</style></head><body>');
945
- h.push('<h1>智能家居配置报价单</h1>');
946
- h.push('<div class="sub">' + esc(title) + ' | 报价日期:' + ds + ' | 服务费率:' + state.fee + '%</div>');
947
- h.push('<table><tr><th style="width:34pt">序号</th><th>产品名称</th><th style="width:34pt">单位</th><th style="width:56pt">单价(元)</th><th style="width:40pt">数量</th><th style="width:66pt">小计(元)</th></tr>');
948
- var n = 0, cur = '', gsum = 0, gcount = 0, rows = state.bill;
949
- function flushGroup(){
950
- if(gcount > 1){
951
- h.push('<tr class="sub2"><td colspan="5" class="r">小计</td><td class="r">' + (Math.round(gsum*100)/100).toFixed(2) + '</td></tr>');
952
- }
953
- gsum = 0; gcount = 0;
954
- }
955
- for(var i=0; i<rows.length; i++){
956
- var b = rows[i], key = b.ci + '_' + b.si;
957
- if(key !== cur){
958
- flushGroup();
959
- cur = key;
960
- h.push('<tr class="grp"><td colspan="6">' + esc(b.cat) + (b.sub ? ' · ' + esc(b.sub) : '') + '</td></tr>');
961
- }
962
- n++;
963
- var amt = b.qty * b.price;
964
- gsum += amt; gcount++;
965
- h.push('<tr><td class="c">' + n + '</td><td>' + esc(b.name) + '</td><td class="c">' + esc(b.unit) + '</td>'
966
- + '<td class="r">' + (+b.price).toFixed(2) + '</td><td class="c">' + b.qty + '</td>'
967
- + '<td class="r">' + amt.toFixed(2) + '</td></tr>');
968
- }
969
- flushGroup();
970
- h.push('<tr class="sub2"><td colspan="5" class="r">设备总价</td><td class="r">' + sub.toFixed(2) + '</td></tr>');
971
- h.push('<tr class="sub2"><td colspan="5" class="r">设计·跟进·安装调试费(' + state.fee + '%)</td><td class="r">' + feeAmt.toFixed(2) + '</td></tr>');
972
- h.push('<tr class="tot"><td colspan="5" class="r">合计(含安装调试)</td><td class="r">' + (sub + feeAmt).toFixed(2) + '</td></tr>');
973
- h.push('</table>');
974
- h.push('<div class="note">备注:<br>1. 以上报价含设备、设计、跟进与安装调试,最终以现场实测为准。<br>2. 本报价有效期 30 天,签字确认后生效。<br>3. 报价单号:' + stamp() + '-' + (state.bill.length) + '</div>');
975
- h.push('</body></html>');
976
- var html = h.join('\r\n');
977
- var name = safeName(title) + '_' + stamp() + '.doc';
978
- var blob = new Blob(['\ufeff' + html], { type:'application/msword;charset=utf-8' });
979
- if(isWX){
980
- copyText(toPlainText());
981
- alert('微信内不能直接下载文件。\n报价单文本已复制,粘贴到 Word / WPS 里即可;\n或点右上角「…」选择「在浏览器打开」后重新导出。');
982
- return;
983
- }
984
- if(dl(blob, name)){ toast('已导出:' + name); }
985
- else{ toast('导出被浏览器拦截,请改用「打印 / 另存为 PDF」'); }
986
- }
987
- function toPlainText(){
988
- var h = [], sub = 0;
989
- h.push('智能家居配置报价单');
990
- h.push(quoteTitle());
991
- state.bill.forEach(function(b, i){
992
- var amt = b.qty * b.price; sub += amt;
993
- h.push((i+1) + '. ' + b.name + ' ' + b.price + '元/' + b.unit + ' × ' + b.qty + ' = ' + (Math.round(amt*100)/100) + '元');
994
- });
995
- var feeAmt = sub * state.fee / 100;
996
- h.push('设备总价:' + Math.round(sub*100)/100 + ' 元');
997
- h.push('设计·跟进·安装调试费(' + state.fee + '%):' + Math.round(feeAmt*100)/100 + ' 元');
998
- h.push('合计:' + Math.round((sub+feeAmt)*100)/100 + ' 元');
999
- return h.join('\r\n');
1000
- }
1001
- function exportCSV(){
1002
- if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
1003
- var lines = ['板块,子类,产品名称,单位,单价,数量,小计'], sub = 0;
1004
- state.bill.forEach(function(b){
1005
- var s = b.qty * b.price; sub += s;
1006
- lines.push([b.cat, b.sub, b.name, b.unit, b.price, b.qty, Math.round(s*100)/100].map(function(v){
1007
- v = String(v == null ? '' : v);
1008
- return /[",\r\n]/.test(v) ? '"' + v.replace(/"/g, '""') + '"' : v;
1009
- }).join(','));
1010
- });
1011
- var feeAmt = sub * state.fee / 100;
1012
- lines.push('设备总价,,,,,' + Math.round(sub*100)/100);
1013
- lines.push('服务费(' + state.fee + '%),,,,,' + Math.round(feeAmt*100)/100);
1014
- lines.push('合计,,,,,' + Math.round((sub+feeAmt)*100)/100);
1015
- var blob = new Blob(['\ufeff' + lines.join('\r\n')], { type:'text/csv;charset=utf-8' });
1016
- var name = safeName(quoteTitle()) + '_' + stamp() + '.csv';
1017
- if(isWX){ copyText(lines.join('\n')); alert('微信内不能下载,CSV 内容已复制到剪贴板。'); return; }
1018
- if(dl(blob, name)){ toast('已导出:' + name); }
1019
- else{ toast('导出被拦截,请用「打印 / 另存为 PDF」'); }
1020
- }
1021
- function copyText(txt){
1022
- try{
1023
- var ta = document.createElement('textarea');
1024
- ta.value = txt;
1025
- ta.style.cssText = 'position:fixed;left:-9999px;top:0';
1026
- document.body.appendChild(ta);
1027
- ta.select(); ta.setSelectionRange(0, ta.value.length);
1028
- document.execCommand('copy');
1029
- document.body.removeChild(ta);
1030
- return true;
1031
- }catch(e){ return false; }
1032
- }
1033
-
1034
- /* ---------- 模态框 ---------- */
1035
- function show(id){ $(id).classList.add('on'); }
1036
- function closeModal(id){ $(id).classList.remove('on'); }
1037
- function closeAll(){ document.querySelectorAll('.modal').forEach(function(m){ m.classList.remove('on'); }); }
1038
-
1039
- /* ---------- 事件 ---------- */
1040
- $('nav').addEventListener('click', function(e){
1041
- var el = e.target.closest('.navi'); if(!el) return;
1042
- state.ci = +el.dataset.ci; state.si = -1; state.kw = '';
1043
- $('kw').value = ''; $('kwClr').classList.remove('on');
1044
- renderNav(); renderStage();
1045
- });
1046
- $('chips').addEventListener('click', function(e){
1047
- var el = e.target.closest('.chip'); if(!el) return;
1048
- state.si = +el.dataset.si; renderStage();
1049
- });
1050
- $('plist').addEventListener('click', function(e){
1051
- var del = e.target.closest('[data-pdel]');
1052
- if(del){
1053
- e.stopPropagation();
1054
- var id = del.dataset.pdel, it = null;
1055
- DB.items.forEach(function(x){ if(x.id === id) it = x; });
1056
- if(!it) return;
1057
- if(!confirm('从产品库删除产品「' + it.name + '」?\n删除后不再出现在产品列表(已加入的明细不受影响)。')) return;
1058
- DB.items = DB.items.filter(function(x){ return x.id !== id; });
1059
- saveDB(); renderAll(); renderList();
1060
- if($('mMgr').classList.contains('on')) renderMgr();
1061
- toast('已删除:' + it.name);
1062
- return;
1063
- }
1064
- var card = e.target.closest('.pcard'); if(!card) return;
1065
- var pid = card.dataset.id, target = null;
1066
- DB.items.forEach(function(x){ if(x.id === pid) target = x; });
1067
- if(target){ addProduct(target); toast('已加入:' + target.name); }
1068
- });
1069
- function onKW(){
1070
- state.kw = $('kw').value;
1071
- $('kwClr').classList.toggle('on', !!state.kw);
1072
- renderList();
1073
- }
1074
- var kwTimer = null;
1075
- $('kw').addEventListener('input', function(){
1076
- clearTimeout(kwTimer); kwTimer = setTimeout(onKW, 110);
1077
- });
1078
- $('kwClr').addEventListener('click', function(){
1079
- $('kw').value = ''; state.kw = ''; this.classList.remove('on'); renderList();
1080
- });
1081
- $('billbody').addEventListener('click', function(e){
1082
- var d = e.target.closest('[data-del]');
1083
- if(d){ state.bill.splice(+d.dataset.del, 1); renderBill(); renderList(); return; }
1084
- });
1085
- $('billbody').addEventListener('input', function(e){
1086
- var t = e.target;
1087
- if(t.dataset.qty !== undefined){ state.bill[+t.dataset.qty].qty = Math.max(0, +t.value || 0); updateSums(); saveSess(); }
1088
- else if(t.dataset.price !== undefined){ state.bill[+t.dataset.price].price = Math.max(0, +t.value || 0); updateSums(); saveSess(); }
1089
- });
1090
- $('fee').addEventListener('input', function(){
1091
- state.fee = Math.max(0, Math.min(100, +this.value || 0));
1092
- updateSums(); saveSess();
1093
- });
1094
- $('btnSaveQuote').addEventListener('click', function(){ saveQuote(false); });
1095
- $('ebSave').addEventListener('click', function(){ saveQuote(false); });
1096
- $('ebNew').addEventListener('click', function(){ saveQuote(true); });
1097
- $('ebExit').addEventListener('click', function(){
1098
- state.editing = null; renderEditBar(); saveSess(); toast('已退出编辑,当前明细保留');
1099
- });
1100
- $('btnHist').addEventListener('click', function(){ renderHist(); show('mHist'); });
1101
- $('btnMgr').addEventListener('click', function(){ openMgr(); });
1102
- $('btnWord').addEventListener('click', exportWord);
1103
- $('btnMore').addEventListener('click', function(){ show('mMore'); });
1104
- $('btnClear').addEventListener('click', function(){
1105
- if(!state.bill.length){ toast('明细已经是空的'); return; }
1106
- if(!confirm('清空全部 ' + state.bill.length + ' 条明细?(历史报价不受影响)')) return;
1107
- state.bill = []; state.editing = null; renderBill(); renderList(); renderEditBar(); saveSess(); toast('已清空');
1108
- });
1109
- document.querySelectorAll('[data-close]').forEach(function(b){
1110
- b.addEventListener('click', function(){ closeModal(b.dataset.close); });
1111
- });
1112
- document.querySelectorAll('.modal').forEach(function(m){
1113
- m.addEventListener('click', function(e){ if(e.target === m) m.classList.remove('on'); });
1114
- });
1115
- $('mWord').addEventListener('click', function(){ closeModal('mMore'); exportWord(); });
1116
- $('mCsv').addEventListener('click', function(){ closeModal('mMore'); exportCSV(); });
1117
- $('mCopy').addEventListener('click', function(){
1118
- if(!state.bill.length){ toast('明细为空'); return; }
1119
- copyText(toPlainText());
1120
- closeModal('mMore'); toast('明细已复制,可粘贴到微信/Word');
1121
- });
1122
- $('mPrint').addEventListener('click', function(){ closeModal('mMore'); setTimeout(function(){ window.print(); }, 120); });
1123
-
1124
- /* 历史报价事件 */
1125
- $('histBody').addEventListener('click', function(e){
1126
- var o = e.target.closest('[data-open]');
1127
- if(o){ openQuote(o.dataset.open); return; }
1128
- var r = e.target.closest('[data-rename]');
1129
- if(r){
1130
- var list = histAll(), rec = null;
1131
- list.forEach(function(q){ if(q.id === r.dataset.rename) rec = q; });
1132
- if(!rec) return;
1133
- var v = prompt('新的报价单名称', rec.name);
1134
- if(v === null) return;
1135
- v = String(v).trim(); if(!v) return;
1136
- rec.name = v; histPut(list);
1137
- if(state.editing && state.editing.id === rec.id){ state.editing.name = v; renderEditBar(); saveSess(); }
1138
- renderHist(); toast('已改名');
1139
- return;
1140
- }
1141
- var d = e.target.closest('[data-delq]');
1142
- if(d){
1143
- var arr = histAll(), t = null;
1144
- arr.forEach(function(q){ if(q.id === d.dataset.delq) t = q; });
1145
- if(!t) return;
1146
- if(!confirm('删除历史报价「' + t.name + '」?不可恢复。')) return;
1147
- histPut(arr.filter(function(q){ return q.id !== t.id; }));
1148
- if(state.editing && state.editing.id === t.id){ /* 仍保留明细编辑状态 */ }
1149
- renderHist(); toast('已删除');
1150
- }
1151
- });
1152
-
1153
- /* 产品库管理事件 */
1154
- $('catList').addEventListener('click', function(e){
1155
- var ren = e.target.closest('[data-rencat]');
1156
- if(ren){
1157
- var i = +ren.dataset.rencat, v = prompt('类目名称', DB.cats[i].n);
1158
- if(v === null) return;
1159
- v = String(v).trim(); if(!v) return;
1160
- DB.cats[i].n = v; saveDB(); renderMgr(); renderAll(); toast('已改名');
1161
- return;
1162
- }
1163
- var del = e.target.closest('[data-delcat]');
1164
- if(del){ delCat(+del.dataset.delcat); return; }
1165
- var li = e.target.closest('.mli');
1166
- if(li){ mgr.ci = +li.dataset.ci; mgr.si = 0; renderMgr(); }
1167
- });
1168
- $('subList').addEventListener('click', function(e){
1169
- var ren = e.target.closest('[data-rensub]');
1170
- if(ren){
1171
- var j = +ren.dataset.rensub, cat = DB.cats[mgr.ci];
1172
- var v = prompt('子类名称', cat.subs[j]);
1173
- if(v === null) return;
1174
- v = String(v).trim(); if(!v) return;
1175
- cat.subs[j] = v; saveDB(); renderMgr(); renderAll(); toast('已改名');
1176
- return;
1177
- }
1178
- var del = e.target.closest('[data-delsub]');
1179
- if(del){ delSub(mgr.ci, +del.dataset.delsub); return; }
1180
- var li = e.target.closest('.mli');
1181
- if(li){ mgr.si = +li.dataset.si; renderItemRows(); renderSubList(); }
1182
- });
1183
- $('mgrKw').addEventListener('input', function(){ mgr.kw = this.value; renderItemRows(); });
1184
- $('itemRows').addEventListener('change', function(e){
1185
- var t = e.target, f = t.dataset.f;
1186
- if(!f) return;
1187
- var row = t.closest('.mtr'), id = row.dataset.id, it = null;
1188
- DB.items.forEach(function(x){ if(x.id === id) it = x; });
1189
- if(!it) return;
1190
- if(f === 'price'){ it.price = Math.max(0, +t.value || 0); t.value = it.price; }
1191
- else{
1192
- var v = String(t.value).trim();
1193
- if(!v){ t.value = it[f]; return; }
1194
- it[f] = v;
1195
- }
1196
- saveDB(); renderNav(); renderStage(); renderMgr();
1197
- });
1198
- $('itemRows').addEventListener('click', function(e){
1199
- var d = e.target.closest('[data-delitem]');
1200
- if(!d) return;
1201
- var id = d.dataset.delitem, it = null;
1202
- DB.items.forEach(function(x){ if(x.id === id) it = x; });
1203
- if(!it) return;
1204
- if(!confirm('删除产品「' + it.name + '」?')) return;
1205
- DB.items = DB.items.filter(function(x){ return x.id !== id; });
1206
- saveDB(); renderMgr(); renderAll(); toast('已删除');
1207
- });
1208
- $('btnAddCat').addEventListener('click', function(){
1209
- var v = prompt('新类目名称(如:智能照明)', '');
1210
- if(v === null) return;
1211
- v = String(v).trim(); if(!v) return;
1212
- DB.cats.push({ n: v, subs: ['默认子类'] });
1213
- mgr.ci = DB.cats.length - 1; mgr.si = 0;
1214
- saveDB(); renderMgr(); renderAll(); toast('已新增类目');
1215
- });
1216
- $('btnAddSub').addEventListener('click', function(){
1217
- var cat = DB.cats[mgr.ci];
1218
- if(!cat){ toast('先建一个类目'); return; }
1219
- var v = prompt('新子类名称', '');
1220
- if(v === null) return;
1221
- v = String(v).trim(); if(!v) return;
1222
- cat.subs.push(v);
1223
- mgr.si = cat.subs.length - 1;
1224
- saveDB(); renderMgr(); renderAll(); toast('已新增子类');
1225
- });
1226
- $('btnAddItem').addEventListener('click', function(){
1227
- var cat = DB.cats[mgr.ci];
1228
- if(!cat){ toast('先建一个类目'); return; }
1229
- if(cat.subs.length === 0){ cat.subs.push('默认子类'); mgr.si = 0; }
1230
- if(mgr.si < 0) mgr.si = 0;
1231
- $('addRow').style.display = 'flex';
1232
- $('anName').value = ''; $('anUnit').value = '个'; $('anPrice').value = '';
1233
- $('anName').focus();
1234
- });
1235
- $('anCancel').addEventListener('click', function(){ $('addRow').style.display = 'none'; });
1236
- $('anSave').addEventListener('click', function(){
1237
- var nm = String($('anName').value).trim(), un = String($('anUnit').value).trim() || '个', pr = Math.max(0, +$('anPrice').value || 0);
1238
- if(!nm){ toast('产品名称不能为空'); return; }
1239
- DB.seq = (DB.seq || 0) + 1;
1240
- DB.items.push({ id:'u' + DB.seq + '_' + Date.now().toString(36), ci: mgr.ci, si: mgr.si, name: nm, unit: un, price: pr, alias: [] });
1241
- saveDB(); $('addRow').style.display = 'none'; renderMgr(); renderAll(); toast('已新增产品:' + nm);
1242
- });
1243
- /* ---------- 产品库备份 / 恢复 ---------- */
1244
- function stamp(){
1245
- var d = new Date(), p = function(n){ return (n < 10 ? '0' : '') + n; };
1246
- return d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + '_' + p(d.getHours()) + p(d.getMinutes());
1247
- }
1248
- $('btnExpDB').addEventListener('click', function(){
1249
- var payload = { type:'slzj-db', ver:1, rev:REV, exportedAt:new Date().toISOString(), db:DB };
1250
- var blob = new Blob([JSON.stringify(payload, null, 1)], { type:'application/json;charset=utf-8' });
1251
- dl(blob, '树懒产品库备份_' + stamp() + '.json');
1252
- toast('已导出备份:' + DB.cats.length + ' 类 / ' + DB.items.length + ' 款产品');
1253
- });
1254
- $('btnImpDB').addEventListener('click', function(){ $('impFile').click(); });
1255
- $('impFile').addEventListener('change', function(e){
1256
- var f = e.target.files && e.target.files[0];
1257
- if(!f) return;
1258
- var fr = new FileReader();
1259
- fr.onload = function(){
1260
- try{
1261
- var j = JSON.parse(String(fr.result));
1262
- var db = (j && j.db) ? j.db : j;
1263
- if(!db || !db.cats || !db.cats.length || !db.items || !db.items.length) throw new Error('不是有效的产品库备份');
1264
- var ni = 0;
1265
- for(var i = 0; i < db.items.length; i++){
1266
- var it = db.items[i];
1267
- if(!it || !it.name) continue;
1268
- if(!it.id) it.id = 'u' + i + '_' + Date.now().toString(36);
1269
- if(!it.unit) it.unit = '个';
1270
- it.price = +it.price || 0;
1271
- if(!it.alias) it.alias = [];
1272
- ni++;
1273
- }
1274
- DB = { rev:REV, cats:db.cats.map(function(c){ return { n:c.n, subs:(c.subs || []).slice() }; }), items:db.items, seq:db.seq || 0 };
1275
- saveDB();
1276
- if(state.ci >= DB.cats.length) state.ci = 0;
1277
- state.si = -1;
1278
- renderAll(); renderMgr();
1279
- toast('导入成功:' + DB.cats.length + ' 类 / ' + ni + ' 款产品');
1280
- }catch(err){
1281
- toast('导入失败:' + (err && err.message ? err.message : '文件格式不对'));
1282
- }
1283
- };
1284
- fr.readAsText(f, 'utf-8');
1285
- e.target.value = '';
1286
- });
1287
-
1288
- $('btnResetDB').addEventListener('click', function(){
1289
- if(!confirm('恢复初始产品库?\n你新增/删除/修改的产品和类目都会丢失,历史报价不受影响。')) return;
1290
- try{ localStorage.removeItem(K_DB); }catch(e){}
1291
- location.reload();
1292
- });
1293
-
1294
- /* ================= 云同步:一份产品库,多人共用 ================= */
1295
- function b64e(s){ try{ return btoa(unescape(encodeURIComponent(s))).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,''); }catch(e){ return ''; } }
1296
- function b64d(s){ try{ return decodeURIComponent(escape(atob(String(s).replace(/-/g,'+').replace(/_/g,'/')))); }catch(e){ return ''; } }
1297
-
1298
- var CFG = (function(){
1299
- var c = null;
1300
- try{ var r = localStorage.getItem(K_CLOUD); if(r) c = JSON.parse(r); }catch(e){}
1301
- /* 扫码链接里带着配置(?c=...)时优先用链接的,客户无需任何设置 */
1302
- try{
1303
- var m = location.search.match(/[?&]c=([^&]+)/);
1304
- if(m){
1305
- var q = JSON.parse(b64d(m[1]));
1306
- if(q && q.url && q.key && q.room){
1307
- c = { url: q.url, key: q.key, room: q.room };
1308
- try{ localStorage.setItem(K_CLOUD, JSON.stringify(c)); }catch(e){}
1309
- }
1310
- }
1311
- }catch(e){}
1312
- if(!c && CLOUD_DEFAULT.url && CLOUD_DEFAULT.key && CLOUD_DEFAULT.room) c = CLOUD_DEFAULT;
1313
- return c;
1314
- })();
1315
- var cloudSkip = false, cloudBusy = false, cloudTimer = null, cloudWarned = false;
1316
-
1317
- function cloudOn(){ return !!(CFG && CFG.url && CFG.key && CFG.room); }
1318
- function cloudTS(){ try{ return +(localStorage.getItem(K_DBTS) || 0); }catch(e){ return 0; } }
1319
-
1320
- function cloudBadge(cls, txt){
1321
- var b = $('btnSync');
1322
- if(b){ b.className = 'btn ghost sb-' + cls; b.innerHTML = '<i class="sdot"></i>' + txt; }
1323
- var st = $('clState');
1324
- if(st){ st.className = 'cst ' + cls; st.textContent = txt; }
1325
- }
1326
- function cloudBase(){
1327
- var u = String(CFG.url || '').replace(/\s+/g, '').replace(/\/+$/, '');
1328
- if(!/\/rest\/v1$/.test(u)) u += '/rest/v1';
1329
- return u + '/slzj_db';
1330
- }
1331
- function cloudHdr(extra){
1332
- var h = { 'apikey': CFG.key, 'Authorization': 'Bearer ' + CFG.key, 'Content-Type': 'application/json' };
1333
- if(extra){ for(var k in extra) h[k] = extra[k]; }
1334
- return h;
1335
- }
1336
-
1337
- /* 把本机产品库上传(用户增删改后自动触发) */
1338
- function cloudPush(){
1339
- if(!cloudOn() || cloudSkip || cloudBusy) return;
1340
- cloudBusy = true; cloudBadge('ing', '同步中');
1341
- var ts = new Date().toISOString();
1342
- fetch(cloudBase() + '?on_conflict=room', {
1343
- method: 'POST',
1344
- cache: 'no-store',
1345
- headers: cloudHdr({ 'Prefer': 'resolution=merge-duplicates,return=minimal' }),
1346
- body: JSON.stringify([{ room: CFG.room, payload: DB, updated_at: ts }])
1347
- }).then(function(r){
1348
- if(!r.ok) return r.text().then(function(t){ throw new Error('HTTP ' + r.status + (t ? ' ' + t.slice(0, 90) : '')); });
1349
- cloudBusy = false;
1350
- try{ localStorage.setItem(K_DBTS, String(Date.parse(ts))); }catch(e){}
1351
- cloudWarned = false;
1352
- cloudBadge('ok', '已同步');
1353
- }).catch(function(e){
1354
- cloudBusy = false;
1355
- cloudBadge('err', '未同步');
1356
- if(!cloudWarned){ cloudWarned = true; toast('云同步失败:' + e.message); }
1357
- });
1358
- }
1359
-
1360
- /* 用云端数据覆盖本机 */
1361
- function cloudApply(p){
1362
- if(!p || !p.cats || !p.cats.length || !p.items || !p.items.length) return false;
1363
- cloudSkip = true;
1364
- DB = { rev: REV, cats: p.cats, items: p.items, seq: p.seq || 0 };
1365
- saveDB();
1366
- cloudSkip = false;
1367
- if(state.ci >= DB.cats.length) state.ci = 0;
1368
- state.si = -1;
1369
- try{ renderAll(); }catch(e){}
1370
- try{ renderMgr(); }catch(e){}
1371
- return true;
1372
- }
1373
-
1374
- /* 从云端拉取,云端更新则覆盖本机,本机更新则上传 */
1375
- function cloudPull(manual){
1376
- if(!cloudOn()){ if(manual) toast('请先填写并保存云端地址与密钥'); return; }
1377
- if(cloudBusy){ if(manual) toast('正在同步,请稍候'); return; }
1378
- cloudBusy = true;
1379
- if(manual) cloudBadge('ing', '拉取中');
1380
- var url = cloudBase() + '?select=payload,updated_at&room=eq.' + encodeURIComponent(CFG.room);
1381
- fetch(url, { headers: cloudHdr(), cache: 'no-store' }).then(function(r){
1382
- if(!r.ok) throw new Error('HTTP ' + r.status);
1383
- return r.json();
1384
- }).then(function(rows){
1385
- cloudBusy = false;
1386
- if(!rows || !rows.length){ cloudPush(); return; }
1387
- var row = rows[0], rp = row.payload, rts = Date.parse(row.updated_at) || 0;
1388
- var same = JSON.stringify(rp && rp.items) === JSON.stringify(DB.items) &&
1389
- JSON.stringify(rp && rp.cats) === JSON.stringify(DB.cats);
1390
- if(same){
1391
- try{ localStorage.setItem(K_DBTS, String(Math.max(rts, cloudTS()))); }catch(e){}
1392
- cloudBadge('ok', '已同步');
1393
- if(manual) toast('已是最新版本');
1394
- return;
1395
- }
1396
- if(rts > cloudTS() + 1000){
1397
- if(cloudApply(rp)){
1398
- try{ localStorage.setItem(K_DBTS, String(rts)); }catch(e){}
1399
- cloudBadge('ok', '已更新');
1400
- toast('产品库已同步到最新(' + DB.items.length + ' 款)');
1401
- }else{
1402
- cloudBadge('err', '云端数据异常');
1403
- }
1404
- }else{
1405
- cloudPush();
1406
- }
1407
- }).catch(function(e){
1408
- cloudBusy = false;
1409
- cloudBadge('err', '未连接');
1410
- if(manual) toast('拉取失败:' + e.message);
1411
- });
1412
- }
1413
-
1414
- function cloudStart(){
1415
- if(cloudTimer){ clearInterval(cloudTimer); cloudTimer = null; }
1416
- if(!cloudOn()){ cloudBadge('off', '未开启'); return; }
1417
- cloudBadge('ing', '连接中');
1418
- cloudPull(false);
1419
- cloudTimer = setInterval(function(){ cloudPull(false); }, 20000);
1420
- }
1421
-
1422
- function cloudFillForm(){
1423
- $('clUrl').value = CFG ? (CFG.url || '') : '';
1424
- $('clKey').value = CFG ? (CFG.key || '') : '';
1425
- $('clRoom').value = CFG ? (CFG.room || '') : 'slzj';
1426
- }
1427
-
1428
- $('btnSync').addEventListener('click', function(){ cloudFillForm(); show('mCloud'); });
1429
-
1430
- $('clSave').addEventListener('click', function(){
1431
- var u = $('clUrl').value.replace(/\s+/g, ''), k = $('clKey').value.replace(/\s+/g, ''), rm = $('clRoom').value.replace(/\s+/g, '');
1432
- if(!/^https?:\/\//.test(u)){ toast('云端地址要以 https:// 开头'); return; }
1433
- if(k.length < 20){ toast('接口密钥看起来不完整'); return; }
1434
- if(!rm){ toast('请填写同步口令'); return; }
1435
- CFG = { url: u, key: k, room: rm };
1436
- try{ localStorage.setItem(K_CLOUD, JSON.stringify(CFG)); }catch(e){}
1437
- closeModal('mCloud');
1438
- cloudWarned = false;
1439
- cloudStart();
1440
- toast('云同步已开启:' + rm);
1441
- });
1442
-
1443
- $('clPush').addEventListener('click', function(){
1444
- if(!cloudOn()){ toast('请先保存云端地址与密钥'); return; }
1445
- cloudWarned = false;
1446
- cloudPush();
1447
- });
1448
-
1449
- $('clPull').addEventListener('click', function(){ cloudPull(true); });
1450
-
1451
- $('clShare').addEventListener('click', function(){
1452
- if(!cloudOn()){ toast('请先保存云端地址与密钥'); return; }
1453
- var base = location.href.split('#')[0].split('?')[0];
1454
- var link = base + '?c=' + b64e(JSON.stringify(CFG));
1455
- if(/^file:/.test(base)){
1456
- $('clTip').innerHTML = '当前是本地文件方式打开,这个链接只能本机用。把页面放到网址上(如局域网服务或公网托管)后再来生成,客户扫码即可自动同步。';
1457
- return;
1458
- }
1459
- $('clTip').innerHTML = '分享链接已复制,做成二维码给客户扫:<br><b style="word-break:break-all;color:#1c4e80">' + esc(link) + '</b>';
1460
- copyText(link);
1461
- toast('分享链接已复制');
1462
- });
1463
-
1464
- $('clOff').addEventListener('click', function(){
1465
- if(!cloudOn()){ toast('当前未开启云同步'); return; }
1466
- if(!confirm('关闭云同步?\n关闭后本机产品库照常可用,但不会再与其他设备同步。')) return;
1467
- CFG = null;
1468
- try{ localStorage.removeItem(K_CLOUD); }catch(e){}
1469
- if(cloudTimer){ clearInterval(cloudTimer); cloudTimer = null; }
1470
- cloudBadge('off', '未开启');
1471
- closeModal('mCloud');
1472
- toast('已关闭云同步');
1473
- });
1474
-
1475
- document.addEventListener('visibilitychange', function(){
1476
- if(!document.hidden && cloudOn()) cloudPull(false);
1477
- });
1478
-
1479
- /* ---------- 启动 ---------- */
1480
- loadSess();
1481
- if(state.ci >= DB.cats.length) state.ci = 0;
1482
- $('fee').value = state.fee;
1483
- renderAll();
1484
- cloudStart();
1485
-
1486
-
1487
- /* ---------- PWA 注册(仅 http/https 生效,本地文件模式自动跳过) ---------- */
1488
- (function(){
1489
- if(!('serviceWorker' in navigator)) return;
1490
- if(!/^https?:$/.test(location.protocol)) return;
1491
- window.addEventListener('load', function(){
1492
- navigator.serviceWorker.register('sw.js').catch(function(){});
1493
- });
1494
- })();
1495
-
1496
- </script>
1497
- </body>
1498
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
6
+ <meta name="theme-color" content="#1c4e80">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="mobile-web-app-capable" content="yes">
9
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
10
+ <meta name="apple-mobile-web-app-title" content="树懒智汇家报价系统">
11
+ <meta name="application-name" content="树懒智汇家报价系统">
12
+ <meta name="format-detection" content="telephone=no">
13
+ <title>树懒智汇家报价系统</title>
14
+ <link rel="manifest" href="manifest.webmanifest">
15
+ <link rel="apple-touch-icon" href="apple-touch-icon.png">
16
+ <link rel="icon" type="image/png" href="icon-192.png">
17
+ <style>
18
+
19
+ *{box-sizing:border-box;margin:0;padding:0}
20
+ :root{
21
+ --ink:#0f2136; --ink2:#41546b; --line:#e3e9f0; --bg:#f4f6f9;
22
+ --brand:#1c4e80; --brand2:#2f6fb0; --accent:#0e7c66; --warn:#b4232a;
23
+ --card:#fff; --radius:12px;
24
+ }
25
+ html,body{height:100%}
26
+ body{
27
+ font-family:"Microsoft YaHei UI","Segoe UI","PingFang SC",system-ui,sans-serif;
28
+ color:var(--ink); background:var(--bg); display:flex; flex-direction:column; height:100vh; overflow:hidden;
29
+ font-size:13.5px; -webkit-font-smoothing:antialiased;
30
+ }
31
+ /* ---------- header ---------- */
32
+ header{
33
+ display:flex; align-items:center; gap:14px; padding:0 18px; height:56px; flex:0 0 56px;
34
+ background:linear-gradient(90deg,#153a5e,#1c4e80 60%,#2f6fb0); color:#fff; box-shadow:0 2px 10px rgba(15,33,54,.16);
35
+ }
36
+ .brand{display:flex; align-items:center; gap:9px; font-size:15px; font-weight:600; letter-spacing:.3px; white-space:nowrap}
37
+ .brand .dot{width:9px;height:9px;border-radius:50%;background:#5fd3b0;box-shadow:0 0 0 3px rgba(95,211,176,.25)}
38
+ .brand em{font-style:normal;font-size:11.5px;font-weight:400;color:#bcd4ea;letter-spacing:0}
39
+ .search{flex:1; max-width:420px; position:relative}
40
+ .search input{
41
+ width:100%; height:34px; border-radius:8px; border:1px solid rgba(255,255,255,.22);
42
+ background:rgba(255,255,255,.14); color:#fff; padding:0 34px 0 32px; font-size:13px; outline:none;
43
+ }
44
+ .search input::placeholder{color:#c3d6e8}
45
+ .search input:focus{background:rgba(255,255,255,.22);border-color:rgba(255,255,255,.45)}
46
+ .search .ico{position:absolute;left:10px;top:9px;font-size:13px;color:#c3d6e8}
47
+ .search .clr{position:absolute;right:8px;top:6px;width:22px;height:22px;border:0;border-radius:50%;background:transparent;color:#cfe0ef;cursor:pointer;font-size:14px;display:none}
48
+ .search .clr.on{display:block}
49
+ .hdr-right{display:flex;align-items:center;gap:10px;margin-left:auto;white-space:nowrap}
50
+ .fee{display:flex;align-items:center;gap:6px;font-size:12.5px;color:#d7e6f4;background:rgba(255,255,255,.12);padding:5px 10px;border-radius:8px}
51
+ .fee input{width:46px;height:22px;border:0;border-radius:5px;background:rgba(255,255,255,.9);color:var(--ink);text-align:center;font-size:13px;font-weight:600;outline:none}
52
+ .btn{height:32px;padding:0 13px;border-radius:8px;border:1px solid transparent;font-size:12.5px;cursor:pointer;font-family:inherit;transition:.15s}
53
+ .btn.ghost{background:rgba(255,255,255,.14);color:#fff;border-color:rgba(255,255,255,.25)}
54
+ .btn.ghost:hover{background:rgba(255,255,255,.26)}
55
+ .btn.ghost.danger:hover{background:#8f1d22;border-color:#8f1d22}
56
+ /* ---------- layout ---------- */
57
+ main{flex:1; display:grid; grid-template-columns:196px 1fr 372px; min-height:0; overflow:hidden}
58
+ /* ---------- nav ---------- */
59
+ nav{background:#fff;border-right:1px solid var(--line);overflow-y:auto;padding:10px 8px 22px}
60
+ .nav-t{font-size:11px;color:#8ba0b6;letter-spacing:1.2px;padding:8px 10px 6px;font-weight:600}
61
+ .navi{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:9px;cursor:pointer;color:var(--ink2);margin-bottom:2px;position:relative;transition:.12s}
62
+ .navi:hover{background:#eef3f9;color:var(--ink)}
63
+ .navi.on{background:linear-gradient(90deg,#e7f0fa,#f2f7fc);color:var(--brand);font-weight:600}
64
+ .navi.on::before{content:"";position:absolute;left:0;top:9px;bottom:9px;width:3px;border-radius:2px;background:var(--brand2)}
65
+ .navi .idx{width:20px;height:20px;border-radius:6px;background:#e8eef5;color:#7c93aa;font-size:11px;display:flex;align-items:center;justify-content:center;font-weight:700;flex:0 0 20px}
66
+ .navi.on .idx{background:var(--brand2);color:#fff}
67
+ .navi .n{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
68
+ .navi .c{font-size:11px;color:#9fb2c6;font-variant-numeric:tabular-nums}
69
+ /* ---------- stage ---------- */
70
+ #stage{display:flex;flex-direction:column;min-width:0;overflow:hidden;background:var(--bg)}
71
+ .stage-top{flex:0 0 auto;padding:12px 16px 4px;background:#fff;border-bottom:1px solid var(--line)}
72
+ .sh{display:flex;align-items:baseline;gap:10px;margin-bottom:9px}
73
+ .sh h2{font-size:15px;font-weight:700;letter-spacing:.2px}
74
+ .sh span{font-size:11.5px;color:#8ba0b6}
75
+ .chips{display:flex;flex-wrap:wrap;gap:6px;padding-bottom:11px;max-height:96px;overflow-y:auto}
76
+ .chip{padding:5px 11px;border-radius:14px;background:#f0f3f7;color:var(--ink2);font-size:12px;cursor:pointer;border:1px solid transparent;transition:.12s;white-space:nowrap}
77
+ .chip:hover{background:#e4ebf4}
78
+ .chip.on{background:var(--brand);color:#fff;border-color:var(--brand)}
79
+ .chip .cn{opacity:.62;margin-left:4px;font-size:11px}
80
+ .plist{flex:1;overflow-y:auto;padding:14px 16px 30px;display:grid;grid-template-columns:repeat(auto-fill,minmax(214px,1fr));grid-auto-rows:max-content;gap:10px;align-content:start}
81
+ .pcard{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:11px 12px;cursor:pointer;position:relative;transition:.14s;overflow:hidden}
82
+ .pcard:hover{border-color:#b9cee4;box-shadow:0 5px 16px rgba(28,78,128,.12);transform:translateY(-1px)}
83
+ .pcard .pn{font-size:13px;font-weight:600;line-height:1.42;margin-bottom:7px;min-height:37px;word-break:break-all;padding-right:22px}
84
+ .pcard .pm{display:flex;align-items:flex-end;justify-content:space-between}
85
+ .pcard .pp{font-size:16.5px;font-weight:700;color:var(--brand);font-variant-numeric:tabular-nums}
86
+ .pcard .pp small{font-size:11px;font-weight:400;color:#93a6ba;margin-left:2px}
87
+ .pcard .add{width:24px;height:24px;border-radius:7px;background:#eef4fa;color:var(--brand2);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;font-weight:600;flex:0 0 24px;transition:.14s}
88
+ .pcard:hover .add{background:var(--brand2);color:#fff}
89
+ .pcard .alias{position:absolute;top:7px;right:7px;font-size:9.5px;background:#f0f7f4;color:var(--accent);border-radius:4px;padding:1px 4px;font-weight:600}
90
+ .pcard.added{border-color:#a8d8c9;background:#f7fdfb}
91
+ .empty{grid-column:1/-1;text-align:center;color:#9db0c4;padding:52px 0;font-size:13px}
92
+ /* ---------- bill ---------- */
93
+ #bill{background:#fff;border-left:1px solid var(--line);display:flex;flex-direction:column;min-width:0}
94
+ .bill-head{flex:0 0 auto;padding:13px 15px 11px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:8px}
95
+ .bill-head b{font-size:14px}
96
+ .bill-head .cnt{font-size:11.5px;color:#8ba0b6;margin-left:auto;font-variant-numeric:tabular-nums}
97
+ .billbody{flex:1;overflow-y:auto;padding:8px 12px 16px}
98
+ .bgrp{margin-bottom:6px}
99
+ .bgrp-h{display:flex;align-items:center;font-size:11.5px;color:#8ba0b6;padding:7px 3px 5px;font-weight:600;letter-spacing:.4px}
100
+ .bgrp-h i{font-style:normal;width:5px;height:5px;border-radius:50%;background:#a9bdd2;margin-right:6px}
101
+ .bgrp-h em{font-style:normal;margin-left:auto;font-variant-numeric:tabular-nums}
102
+ .row{display:grid;grid-template-columns:1fr auto;gap:3px 6px;padding:8px 9px;border:1px solid #eef2f7;border-radius:9px;margin-bottom:5px;background:#fcfdfe}
103
+ .row:hover{border-color:#dde7f1;background:#fbfdff}
104
+ .row .rn{font-size:12.5px;line-height:1.4;grid-column:1/-1;word-break:break-all;padding-right:16px}
105
+ .row .ru{font-size:11px;color:#9db0c4;grid-column:1/-1;margin-top:-1px}
106
+ .row .calc{grid-column:1/-1;display:flex;align-items:center;gap:6px;margin-top:2px}
107
+ .row input{border:1px solid #e1e8f0;border-radius:6px;height:26px;font-size:12.5px;text-align:center;font-family:inherit;color:var(--ink);outline:none;background:#fff;font-variant-numeric:tabular-nums}
108
+ .row input:focus{border-color:var(--brand2);box-shadow:0 0 0 2px rgba(47,111,176,.12)}
109
+ .row .ip{width:64px}
110
+ .row .iq{width:52px}
111
+ .row .x{font-size:11px;color:#a8b8c8}
112
+ .row .amt{font-size:13px;font-weight:700;color:var(--brand);margin-left:auto;font-variant-numeric:tabular-nums}
113
+ .row .del{position:absolute;opacity:0}
114
+ .row{position:relative}
115
+ .row .delbtn{position:absolute;top:6px;right:6px;width:18px;height:18px;border:0;border-radius:5px;background:transparent;color:#b9c8d6;cursor:pointer;font-size:13px;line-height:1;display:flex;align-items:center;justify-content:center}
116
+ .row .delbtn:hover{background:#fdeaea;color:var(--warn)}
117
+ .bempty{text-align:center;color:#a8b8c8;font-size:12.5px;padding:46px 20px;line-height:2}
118
+ .bill-foot{flex:0 0 auto;border-top:1px solid var(--line);padding:12px 15px 14px;background:#fafcfe}
119
+ .fl{display:flex;align-items:center;font-size:12.5px;color:var(--ink2);padding:3px 0}
120
+ .fl em{font-style:normal;margin-left:auto;font-variant-numeric:tabular-nums;font-weight:600;color:var(--ink)}
121
+ .fl.total{font-size:14px;padding-top:8px;margin-top:5px;border-top:1px dashed var(--line);color:var(--ink);font-weight:700}
122
+ .fl.total em{font-size:20px;color:var(--brand);font-weight:800}
123
+ .fl .tag{font-size:11px;color:#8ba0b6;margin-left:5px;font-weight:400}
124
+ /* ---------- toast ---------- */
125
+ #toast{position:fixed;left:50%;bottom:34px;transform:translate(-50%,20px);background:#0f2136;color:#fff;padding:9px 18px;border-radius:9px;font-size:12.5px;opacity:0;pointer-events:none;transition:.22s;z-index:99;box-shadow:0 8px 24px rgba(15,33,54,.3)}
126
+ #toast.on{opacity:1;transform:translate(-50%,0)}
127
+ /* ---------- scrollbar ---------- */
128
+ ::-webkit-scrollbar{width:8px;height:8px}
129
+ ::-webkit-scrollbar-thumb{background:#cfdae6;border-radius:4px}
130
+ ::-webkit-scrollbar-thumb:hover{background:#b6c6d8}
131
+ ::-webkit-scrollbar-track{background:transparent}
132
+ /* ---------- responsive ---------- */
133
+ @media(max-width:1180px){
134
+ main{grid-template-columns:160px 1fr 330px}
135
+ }
136
+ @media(max-width:900px){
137
+ body{font-size:13px}
138
+ main{grid-template-columns:1fr;grid-template-rows:auto 1fr auto}
139
+ nav{display:flex;overflow-x:auto;border-right:0;border-bottom:1px solid var(--line);padding:7px 8px;gap:5px}
140
+ .nav-t{display:none}
141
+ .navi{flex:0 0 auto;padding:7px 11px;margin:0;background:#f2f6fa}
142
+ .navi .idx{display:none}
143
+ .navi .c{display:none}
144
+ .navi.on::before{display:none}
145
+ #bill{max-height:44vh;border-left:0;border-top:1px solid var(--line)}
146
+ .plist{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
147
+ .hdr-right .fee{display:none}
148
+ }
149
+
150
+
151
+ /* ============ v7:树懒智汇家报价系统 追加样式 ============ */
152
+ header .btn{background:#5fd3b0;color:#07301d;font-weight:700;border-color:transparent}
153
+ header .btn:hover{background:#79e0c1}
154
+ header .btn.ghost{background:rgba(255,255,255,.08);color:#dbe9e5;font-weight:400}
155
+ header .btn.ghost:hover{background:rgba(255,255,255,.16);color:#fff}
156
+
157
+ /* 产品卡:管理模式删除按钮 */
158
+ .pcard .pdel{position:absolute;top:6px;right:6px;width:20px;height:20px;border-radius:6px;background:#fdeaea;color:#b4232a;border:0;font-size:13px;line-height:1;cursor:pointer;display:none;padding:0;z-index:3}
159
+ .pcard .pdel:hover{background:#d94f4f;color:#fff}
160
+ body.mgr .pcard .pdel{display:block}
161
+ body.mgr .pcard .alias{display:none}
162
+
163
+ /* 正在编辑的报价单提示条 */
164
+ .editbar{display:none;align-items:center;gap:10px;padding:7px 16px;background:#0e7c66;color:#fff;font-size:12.5px;flex:0 0 auto}
165
+ .editbar.on{display:flex}
166
+ .editbar span{font-weight:600}
167
+ .editbar .mini{margin-left:auto;height:24px;padding:0 10px;border-radius:6px;border:1px solid rgba(255,255,255,.45);background:transparent;color:#fff;font-size:12px;cursor:pointer;font-family:inherit}
168
+ .editbar .mini:hover{background:rgba(255,255,255,.16)}
169
+ .editbar .mini+.mini{margin-left:0}
170
+ .editbar .mini.primary{background:#fff;color:#0b634f;border-color:#fff;font-weight:700}
171
+
172
+ /* 模态框 */
173
+ .modal{position:fixed;inset:0;z-index:90;background:rgba(9,24,36,.46);display:none;align-items:center;justify-content:center;padding:16px;backdrop-filter:blur(2px)}
174
+ .modal.on{display:flex}
175
+ .mbox{background:#fff;border-radius:14px;width:100%;max-width:620px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 60px rgba(9,24,36,.3)}
176
+ .mbox.wide{max-width:1000px}
177
+ .mhead{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
178
+ .mhead b{font-size:14.5px}
179
+ .mhead span{font-size:11.5px;color:#8ba0b6}
180
+ .mx{margin-left:auto;width:26px;height:26px;border-radius:7px;border:1px solid var(--line);background:#fff;color:#7f93a8;font-size:15px;line-height:1;cursor:pointer;flex:0 0 auto}
181
+ .mx:hover{background:#f2f6fa;color:var(--ink)}
182
+ .mbody{padding:14px 16px;overflow:auto;flex:1}
183
+ .mfoot{padding:10px 16px;border-top:1px solid var(--line);display:flex;align-items:center;gap:10px;font-size:12px;color:#8ba0b6;flex:0 0 auto}
184
+ .mfoot .mini{margin-left:auto}
185
+ .mbtn{display:block;width:100%;text-align:left;padding:11px 14px;border:1px solid var(--line);border-radius:9px;background:#fff;font-family:inherit;font-size:13px;color:var(--ink);cursor:pointer;margin-bottom:8px}
186
+ .mbtn:hover{background:#f6fafd;border-color:#c9dcea}
187
+ .modal.small .mbox{max-width:400px}
188
+
189
+ /* 历史报价列表 */
190
+ .hitem{display:flex;align-items:center;gap:10px;padding:11px 12px;border:1px solid var(--line);border-radius:10px;margin-bottom:8px}
191
+ .hitem .hi{flex:1;min-width:0}
192
+ .hitem .hn{font-size:13.5px;font-weight:600;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
193
+ .hitem .hm{font-size:11.5px;color:#8ba0b6}
194
+ .hitem .hs{font-size:14px;font-weight:800;color:var(--accent);font-variant-numeric:tabular-nums;white-space:nowrap}
195
+ .hitem .ha{display:flex;gap:6px;flex:0 0 auto}
196
+ .hitem .mini{height:28px;padding:0 10px;border-radius:7px;border:1px solid var(--line);background:#fff;font-family:inherit;font-size:12px;color:var(--ink2);cursor:pointer}
197
+ .hitem .mini:hover{background:#f2f6fa;border-color:#c9dcea}
198
+ .hitem .mini.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
199
+ .hitem .mini.danger{color:#b4232a}
200
+ .hitem .mini.danger:hover{background:#fdeaea;border-color:#f0c4c4}
201
+ .hempty{text-align:center;color:#a8b8c8;font-size:12.5px;padding:44px 16px;line-height:2}
202
+
203
+ /* 产品库管理 三栏 */
204
+ .mgr3{display:grid;grid-template-columns:180px 180px 1fr;gap:10px;min-height:360px}
205
+ .mcol{border:1px solid var(--line);border-radius:10px;display:flex;flex-direction:column;overflow:hidden;min-height:0}
206
+ .mcol-h{display:flex;align-items:center;gap:6px;padding:7px 9px;background:#f7fafc;border-bottom:1px solid var(--line);font-size:12px;font-weight:700;color:var(--ink2);flex:0 0 auto}
207
+ .mcol-h .mini{margin-left:auto;height:22px;padding:0 8px;border-radius:5px;border:1px solid var(--line);background:#fff;color:var(--brand);font-family:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
208
+ .mcol-h .mini:hover{background:#eef5fb;border-color:#c9dcea}
209
+ .mlist{overflow:auto;flex:1;padding:5px}
210
+ .mli{display:flex;align-items:center;gap:5px;padding:6px 7px;border-radius:7px;font-size:12.5px;cursor:pointer;color:var(--ink2)}
211
+ .mli:hover{background:#f4f8fc}
212
+ .mli.on{background:#e8f2fb;color:var(--brand);font-weight:700}
213
+ .mli .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
214
+ .mli .num{font-size:10.5px;color:#a8b8c8;flex:0 0 auto}
215
+ .mli .x{border:0;background:none;color:#c2cfdb;cursor:pointer;font-size:13px;padding:0 1px;flex:0 0 auto;line-height:1}
216
+ .mli .x:hover{color:var(--warn)}
217
+ .mtable{display:flex;flex-direction:column;overflow:auto;flex:1;padding:5px}
218
+ .mtr{display:flex;align-items:center;gap:5px;padding:3px 5px;border-radius:6px;font-size:12px}
219
+ .mtr:hover{background:#f7fafd}
220
+ .mtr input{height:26px;border:1px solid var(--line);border-radius:5px;padding:0 6px;font-size:12px;font-family:inherit;color:var(--ink);min-width:0;outline:none;background:#fff}
221
+ .mtr input:focus{border-color:var(--brand2);box-shadow:0 0 0 2px rgba(47,111,176,.1)}
222
+ .mtr input.nm{flex:1}
223
+ .mtr input.un{width:46px;text-align:center}
224
+ .mtr input.pr{width:66px;text-align:right;font-variant-numeric:tabular-nums}
225
+ .mtr .x{border:0;background:none;color:#c8d4e0;cursor:pointer;font-size:13px;padding:0 2px;line-height:1}
226
+ .mtr .x:hover{color:var(--warn)}
227
+ .mtr.mth{background:#f7fafc;font-size:11px;color:#8ba0b6;font-weight:700;position:sticky;top:0;z-index:2}
228
+ .mtr.mth span:nth-child(1){flex:1}
229
+ .mtr.mth span:nth-child(2){width:46px;text-align:center}
230
+ .mtr.mth span:nth-child(3){width:66px;text-align:right}
231
+ .mtr.mth span:nth-child(4){width:20px}
232
+ .miniInput{height:24px;border:1px solid var(--line);border-radius:5px;padding:0 7px;font-size:11.5px;font-family:inherit;outline:none;width:96px;margin-left:auto}
233
+ .addrow{display:flex;gap:5px;padding:7px;border-bottom:1px solid var(--line);background:#fafcfe;flex:0 0 auto;flex-wrap:wrap}
234
+ .addrow input{height:27px;border:1px solid var(--line);border-radius:5px;padding:0 6px;font-size:12px;font-family:inherit;outline:none}
235
+ .addrow input:first-child{flex:1;min-width:110px}
236
+ .addrow input:nth-child(2){width:52px;text-align:center}
237
+ .addrow input:nth-child(3){width:72px;text-align:right}
238
+ .addrow .mini{height:27px;padding:0 10px;border-radius:5px;border:1px solid var(--line);background:#fff;font-family:inherit;font-size:12px;cursor:pointer}
239
+ .addrow .mini.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
240
+ .mini.danger{color:var(--warn);border-color:#f0c4c4;background:#fff;height:26px;padding:0 10px;border-radius:6px;font-family:inherit;font-size:12px;cursor:pointer}
241
+ .billbtns{display:flex;gap:8px;margin-top:10px}
242
+ .btn.block{flex:1;height:34px;border-radius:8px;border:1px solid var(--brand);background:var(--brand);color:#fff;font-size:13px;font-family:inherit;cursor:pointer;font-weight:600}
243
+ .btn.block:hover{background:#16436e}
244
+ .btn.ghost.block{background:#fff;color:var(--ink2);border-color:var(--line)}
245
+ .btn.ghost.block:hover{background:#f6fafd}
246
+ .btn.ghost.block.danger{color:var(--warn);border-color:#f0c4c4}
247
+
248
+ /* 手机端 */
249
+ @media(max-width:900px){
250
+ header{height:auto;flex:0 0 auto;flex-wrap:wrap;padding:8px 11px;gap:7px}
251
+ .brand{font-size:14px}
252
+ .brand em{display:none}
253
+ .search{order:3;max-width:none;flex:1 0 100%}
254
+ .hdr-right{margin-left:0;flex:1 0 100%;gap:7px;overflow-x:auto;padding-bottom:1px}
255
+ .hdr-right .fee{display:flex;flex:0 0 auto}
256
+ .hdr-right .fee input{width:38px}
257
+ header .btn{flex:0 0 auto;height:30px;padding:0 11px;font-size:12px}
258
+ .mgr3{grid-template-columns:1fr;min-height:0}
259
+ .mlist{max-height:150px}
260
+ .mtable{max-height:260px}
261
+ .mbox{max-height:94vh}
262
+ .editbar{flex-wrap:wrap}
263
+ .editbar span{flex:1 0 100%}
264
+ .editbar .mini{margin-left:0}
265
+ .hitem{flex-wrap:wrap}
266
+ .hitem .ha{width:100%;justify-content:flex-end}
267
+ }
268
+ @media print{
269
+ header,#nav,#stage,.editbar,.billbtns,#toast,.modal,.search{display:none!important}
270
+ body{height:auto;overflow:visible;background:#fff}
271
+ main{display:block}
272
+ #bill{border:0}
273
+ }
274
+
275
+ /* 产品卡删除键:桌面悬停显示,手机隐藏防误触 */
276
+ .pcard .pdel{opacity:0;transition:opacity .15s}
277
+ .pcard:hover .pdel{opacity:1}
278
+ @media(max-width:900px){.pcard .pdel{display:none}}
279
+ /* 品牌名 */
280
+ .bname{font-size:15px}
281
+ @media(max-width:900px){.bname{font-size:14px}}
282
+
283
+ /* ===== 云同步 ===== */
284
+ #btnSync .sdot{ width:7px; height:7px; border-radius:50%; background:#9aa7b4; display:inline-block; margin-right:5px; vertical-align:1px; }
285
+ #btnSync.sb-ok .sdot{ background:#17a673; }
286
+ #btnSync.sb-err .sdot{ background:#e0483c; }
287
+ #btnSync.sb-ing .sdot{ background:#f0a53c; }
288
+ .cfgs{ padding:14px 16px; }
289
+ .cfgs .cfgrow{ display:flex; align-items:center; margin:0 0 11px; }
290
+ .cfgs .cfgrow label{ width:70px; flex:none; font-size:13px; color:#4a5a6a; }
291
+ .cfgs .cfgrow input{ flex:1; min-width:0; padding:8px 10px; border:1px solid #d5dde5; border-radius:8px; font-size:13px; background:#fff; color:#22303c; }
292
+ .cfgtip{ font-size:12px; line-height:1.65; color:#7b8896; background:#f4f7fa; border-radius:8px; padding:9px 11px; }
293
+ .cst{ font-size:12px; color:#8b96a3; }
294
+ .cst.ok{ color:#17a673; }
295
+ .cst.err{ color:#e0483c; }
296
+ .cst.ing{ color:#c9821f; }
297
+
298
+ /* 云同步面板底部按钮:窄屏换行不挤压 */
299
+ #mCloud .mfoot{ flex-wrap:wrap; gap:8px 6px; }
300
+ #mCloud .mfoot .cst{ flex:1 0 100%; }
301
+ #mCloud .mfoot .mini{ padding:7px 10px; font-size:12px; }
302
+
303
+ </style>
304
+ </head>
305
+ <body>
306
+ <header>
307
+ <div class="brand"><span class="dot"></span><span class="bname">树懒智汇家报价系统</span><em id="stat"></em></div>
308
+ <div class="search">
309
+ <span class="ico">&#9906;</span>
310
+ <input id="kw" placeholder="搜索产品 / 型号 / 品牌,如 射灯、C2、肤感">
311
+ <button class="clr" id="kwClr">&times;</button>
312
+ </div>
313
+ <div class="hdr-right">
314
+ <div class="fee">服务费率<input id="fee" type="number" value="25" min="0" max="100" step="1">%</div>
315
+ <button class="btn ghost" id="btnHist">历史报价</button>
316
+ <button class="btn ghost" id="btnMgr">产品库</button>
317
+ <button class="btn ghost" id="btnSync" title="打开云同步设置"><i class="sdot"></i>云同步</button>
318
+ <button class="btn" id="btnWord">导出 Word</button>
319
+ <button class="btn ghost" id="btnMore">更多</button>
320
+ </div>
321
+ </header>
322
+ <div id="editbar" class="editbar"><span id="ebText"></span>
323
+ <button class="mini" id="ebSave">保存到本单</button>
324
+ <button class="mini" id="ebNew">另存为新单</button>
325
+ <button class="mini" id="ebExit">退出编辑</button>
326
+ </div>
327
+ <main>
328
+ <nav id="nav"></nav>
329
+ <section id="stage">
330
+ <div class="stage-top">
331
+ <div class="sh"><h2 id="catName"></h2><span id="catMeta"></span></div>
332
+ <div class="chips" id="chips"></div>
333
+ </div>
334
+ <div class="plist" id="plist"></div>
335
+ </section>
336
+ <aside id="bill">
337
+ <div class="bill-head"><b>报价明细</b><span class="cnt" id="cnt"></span></div>
338
+ <div class="billbody" id="billbody"></div>
339
+ <div class="bill-foot">
340
+ <div class="fl"><span>产品小计<i class="tag" id="qtyTag"></i></span><em id="subAmt">&yen;0</em></div>
341
+ <div class="fl"><span>服务费<i class="tag" id="feeTag">25%</i></span><em id="feeAmt">&yen;0</em></div>
342
+ <div class="fl total"><span>合计</span><em id="totAmt">&yen;0</em></div>
343
+ <div class="billbtns">
344
+ <button class="btn block" id="btnSaveQuote">保存报价单</button>
345
+ <button class="btn ghost block danger" id="btnClear">清空明细</button>
346
+ </div>
347
+ </div>
348
+ </aside>
349
+ </main>
350
+
351
+ <!-- 历史报价 -->
352
+ <div class="modal" id="mHist">
353
+ <div class="mbox">
354
+ <div class="mhead"><b>历史报价单</b><span>点“载入”可继续在原单上调整</span><button class="mx" data-close="mHist">&times;</button></div>
355
+ <div class="mbody" id="histBody"></div>
356
+ </div>
357
+ </div>
358
+
359
+ <!-- 产品库管理 -->
360
+ <div class="modal" id="mMgr">
361
+ <div class="mbox wide">
362
+ <div class="mhead"><b>产品库管理</b><span>可新增/删除类目与子类、修改或删除产品,所有改动自动保存在本机</span>
363
+ <button class="mx" data-close="mMgr">&times;</button></div>
364
+ <div class="mbody mgr3">
365
+ <div class="mcol">
366
+ <div class="mcol-h"><b>类目</b><button class="mini" id="btnAddCat">+ 新增类目</button></div>
367
+ <div class="mlist" id="catList"></div>
368
+ </div>
369
+ <div class="mcol">
370
+ <div class="mcol-h"><b id="subTitle">子类</b><button class="mini" id="btnAddSub">+ 新增子类</button></div>
371
+ <div class="mlist" id="subList"></div>
372
+ </div>
373
+ <div class="mcol grow">
374
+ <div class="mcol-h"><b id="itemTitle">产品</b>
375
+ <input id="mgrKw" class="miniInput" placeholder="筛选产品名">
376
+ <button class="mini" id="btnAddItem">+ 新增产品</button>
377
+ </div>
378
+ <div class="addrow" id="addRow" style="display:none">
379
+ <input id="anName" placeholder="产品名称">
380
+ <input id="anUnit" value="个" title="单位">
381
+ <input id="anPrice" type="number" min="0" placeholder="单价">
382
+ <button class="mini primary" id="anSave">保存</button>
383
+ <button class="mini" id="anCancel">取消</button>
384
+ </div>
385
+ <div class="mtable">
386
+ <div class="mtr mth"><span>产品名称</span><span>单位</span><span>单价</span><span></span></div>
387
+ <div id="itemRows"></div>
388
+ </div>
389
+ </div>
390
+ </div>
391
+ <div class="mfoot">
392
+ <span id="mgrTip"></span>
393
+ <button class="mini" id="btnExpDB" title="把当前产品库导出成 json 文件,换手机/换浏览器时用它恢复">导出备份</button>
394
+ <button class="mini" id="btnImpDB" title="从备份文件恢复产品库">导入备份</button>
395
+ <input type="file" id="impFile" accept=".json,application/json,text/plain" style="display:none">
396
+ <button class="mini danger" id="btnResetDB">恢复初始产品库</button>
397
+ </div>
398
+ </div>
399
+ </div>
400
+
401
+ <!-- 云同步 -->
402
+ <div class="modal" id="mCloud">
403
+ <div class="mbox">
404
+ <div class="mhead"><b>云同步</b><span>开启后,本机产品库的增删改会自动上传,其他人扫码进来会自动同步到最新</span><button class="mx" data-close="mCloud">&times;</button></div>
405
+ <div class="mbody cfgs">
406
+ <div class="cfgrow"><label>云端地址</label><input id="clUrl" placeholder="https://xxxx.supabase.co" autocapitalize="off" spellcheck="false"></div>
407
+ <div class="cfgrow"><label>接口密钥</label><input id="clKey" placeholder="anon public key" autocapitalize="off" spellcheck="false"></div>
408
+ <div class="cfgrow"><label>同步口令</label><input id="clRoom" placeholder="同一口令的人共用一份产品库,如 slzj-family" autocapitalize="off" spellcheck="false"></div>
409
+ <div class="cfgtip" id="clTip">填好后点保存,会立即把本机产品库上传到云端;之后任何设备打开都会自动取最新版本。</div>
410
+ </div>
411
+ <div class="mfoot">
412
+ <span id="clState"></span>
413
+ <button class="mini" id="clPull">立即拉取云端</button>
414
+ <button class="mini" id="clPush">上传本机版本</button>
415
+ <button class="mini danger" id="clOff">关闭云同步</button>
416
+ <button class="mini" id="clShare" title="生成带配置的分享链接,客户扫码打开即自动同步">生成分享链接</button>
417
+ <button class="mini primary" id="clSave">保存并启用</button>
418
+ </div>
419
+ </div>
420
+ </div>
421
+
422
+ <!-- 更多 / 导出 -->
423
+ <div class="modal small" id="mMore">
424
+ <div class="mbox">
425
+ <div class="mhead"><b>更多操作</b><button class="mx" data-close="mMore">&times;</button></div>
426
+ <div class="mbody">
427
+ <button class="mbtn" id="mWord">导出 Word 文档(.doc)</button>
428
+ <button class="mbtn" id="mCsv">导出 CSV 表格</button>
429
+ <button class="mbtn" id="mCopy">复制明细到剪贴板</button>
430
+ <button class="mbtn" id="mPrint">打印 / 另存为 PDF</button>
431
+ </div>
432
+ </div>
433
+ </div>
434
+
435
+ <div id="toast"></div>
436
+ <script>
437
+ var DATA = {"cats": [{"n": "智能开关面板", "subs": ["小米智能开关", "肤感玻璃开关", "PC系列开关", "AG玻璃开关", "AE系列", "AK系列", "X6系列", "高端系列", "胡桃木系列", "无线开关", "插座与配件"]}, {"n": "智能照明", "subs": ["射灯", "筒灯", "灯带", "磁吸轨道灯", "驱动电源", "吸顶灯与灯泡", "灯槽型材"]}, {"n": "智能窗帘遮阳", "subs": ["窗帘电机", "窗帘轨道", "卷帘与百叶", "帘布面料", "电动开窗器", "窗帘配件"]}, {"n": "传感与安防", "subs": ["人体传感器", "环境传感器", "门窗与报警", "智能门锁", "摄像头监控"]}, {"n": "中控与语音", "subs": ["中控屏与场景屏", "智能音箱", "智能网关", "遥控器与支架"]}, {"n": "环境与家电", "subs": ["空调控制", "新风地暖", "浴霸暖风", "清洁家电", "晾衣架", "大家电"]}, {"n": "网络覆盖", "subs": ["路由器", "交换机", "AP组网"]}, {"n": "辅材与加工", "subs": ["低压电源", "辅材", "加工与物流"]}], "items": [[0, 0, "小米智能开关Pro(单键)", "个", 129.0], [0, 0, "小米智能开关Pro(双键)", "个", 149.0], [0, 0, "小米智能开关Pro(三键)", "个", 169.0], [0, 0, "米家智能家庭面板", "个", 185.0], [0, 0, "小米智能家庭面板(三键语音屏)(床头双键开关替换)", "个", 329.0], [0, 0, "小米智能家庭面板(零火三键)", "个", 329.0], [0, 0, "小米智能家庭面板(带三键开关)", "个", 329.0], [0, 0, "米家智能家庭面板(入门处开关)", "个", 329.0], [0, 0, "小米智能家庭面板(三键语音屏)", "个", 329.0], [0, 0, "xiaomi智能家庭面板屏pro8", "个", 749.0], [0, 1, "1.8(一块布单键)布帘", "个", 4.6], [0, 1, "2.38(一块布单键)纱帘", "个", 5.64], [0, 1, "开关刻字", "个", 7.0], [0, 1, "(纱帘大)5.695(一块布单键)布帘", "个", 12.53], [0, 1, "智能开关(单键单火版)", "个", 59.0], [0, 1, "智能开关(双键单火版)", "个", 64.0], [0, 1, "智能开关(三键单火版)", "个", 69.0], [0, 1, "米家智能开关(零火单键)肤感", "个", 89.0], [0, 1, "玻璃超薄肤感", "个", 129.0], [0, 1, "智能开关(零火单键)", "个", 79.0], [0, 1, "智能开关(单火单键)", "个", 79.0], [0, 1, "米家智能开关(零火双键)肤感", "个", 99.0], [0, 1, "玻璃超薄肤感", "个", 139.0], [0, 1, "智能开关(零火双键)", "个", 89.0], [0, 1, "智能开关(单火双键)", "个", 89.0], [0, 1, "肤感超薄智能开关(零火单键)", "个", 89.0], [0, 1, "米家智能开关(零火三键)肤感", "个", 109.0], [0, 1, "玻璃超薄肤感", "个", 149.0], [0, 1, "智能开关(零火三键)", "个", 99.0], [0, 1, "智能开关", "个", 99.0], [0, 1, "智能开关(单火三键)", "个", 99.0], [0, 1, "肤感超薄智能开关(零火双键)", "个", 99.0], [0, 1, "肤感超薄智能开关(零火三键)", "个", 109.0], [0, 1, "智能开关(零火四键)", "个", 119.0], [0, 1, "肤感超薄智能开关(零火四键)", "个", 119.0], [0, 1, "肤感玻璃(单火单键)", "个", 129.0], [0, 1, "肤感玻璃(零火单键)", "个", 129.0], [0, 1, "肤感玻璃智能开关(单键)白色", "个", 129.0], [0, 1, "肤感玻璃(单火双键)", "个", 139.0], [0, 1, "肤感玻璃(零火双键)", "个", 139.0], [0, 1, "肤感玻璃智能开关(双键)白色", "个", 139.0], [0, 1, "肤感玻璃(单火三键)", "个", 149.0], [0, 1, "肤感玻璃(零火三键)", "个", 149.0], [0, 1, "肤感玻璃智能开关(三键)白色", "个", 149.0], [0, 1, "肤感玻璃(零火四键)", "个", 159.0], [0, 1, "肤感玻璃智能开关(四键)白色", "个", 159.0], [0, 1, "玻璃超薄肤感智能开关(单火四键)", "个", 169.0], [0, 1, "四键两场景圆屏", "个", 399.0], [0, 1, "智能面板", "个", 649.0], [0, 1, "新风旋钮面板", "个", 899.0], [0, 2, "PC材质智能开关(零火单键)", "个", 69.0], [0, 2, "PTXPC开关(单火单键)", "个", 69.0], [0, 2, "PTXPC开关(单火双键)", "个", 74.0], [0, 2, "PC材质智能开关(零火双键)", "个", 79.0], [0, 2, "PTXPC开关(单火三键)", "个", 79.0], [0, 2, "PC肤感超薄(零火单键)", "个", 79.0], [0, 2, "PC材质智能开关(零火三键)", "个", 89.0], [0, 2, "PC肤感超薄(零火双键)", "个", 89.0], [0, 2, "PC肤感(单键)", "个", 89.0], [0, 2, "PC肤感超薄(零火三键)", "个", 99.0], [0, 2, "PC肤感(双键)", "个", 99.0], [0, 2, "PC肤感超薄(零火四键)", "个", 109.0], [0, 2, "PC肤感(三键)", "个", 109.0], [0, 3, "AG玻璃(零火单键)", "个", 129.0], [0, 3, "AG智能开关(单键)(单火版)", "个", 129.0], [0, 3, "AG智能开关(单键)", "个", 129.0], [0, 3, "AG智能开关(双键零火版)", "个", 139.0], [0, 3, "AG智能开关", "个", 139.0], [0, 3, "AG智能开关(双键)", "个", 139.0], [0, 3, "AG智能开关(双键)(单火版)", "个", 144.0], [0, 3, "AG玻璃(零火三键)", "个", 149.0], [0, 3, "AG智能开关(三键)(单火版)", "个", 149.0], [0, 3, "AG智能开关(三键)", "个", 149.0], [0, 3, "AG玻璃(零火四键)", "个", 159.0], [0, 3, "AG玻璃超薄肤感智能开关(零火单键)", "个", 159.0], [0, 3, "AG玻璃超薄肤感智能开关(零火双键)", "个", 169.0], [0, 3, "AG玻璃超薄肤感智能开关(零火三键)", "个", 179.0], [0, 4, "AE智能开关(单键)(零火版)", "个", 70.0], [0, 4, "AE智能开关(双键)(零火版)", "个", 72.5], [0, 4, "平头熊AE(单火单键)", "个", 79.0], [0, 4, "AE智能开关(单键)", "个", 79.0], [0, 4, "平头熊AE(单火双键)", "个", 89.0], [0, 4, "AE智能开关(双键)", "个", 89.0], [0, 4, "AE智能开关(三键)(零火版)", "个", 90.0], [0, 4, "平头熊AE(单火三键)", "个", 99.0], [0, 4, "AE智能开关(三键开)", "个", 99.0], [0, 4, "AE智能开关(四键)(零火版)", "个", 100.0], [0, 5, "AK智能开关(单键)", "个", 44.0], [0, 5, "平头熊AK(零火单键)", "个", 49.0], [0, 5, "AK智能开关(双键)", "个", 54.0], [0, 5, "平头熊AK(零火双键)", "个", 59.0], [0, 5, "智能开关AK(单键单火版)灰色", "个", 59.0], [0, 5, "智能开关AK(双键单火版)灰色", "个", 64.0], [0, 5, "AK智能开关(三键)", "个", 64.0], [0, 5, "平头熊AK(零火三键)", "个", 69.0], [0, 5, "AK智能开关(零火单键)", "个", 69.0], [0, 5, "智能开关AK(三键单火版)灰色", "个", 69.0], [0, 5, "AK智能开关(零火三键)", "个", 89.0], [0, 5, "平头熊AKPRO(零火单键)", "个", 129.0], [0, 5, "平头熊AKPRO(零火双键)", "个", 149.0], [0, 5, "平头熊AKPRO(零火三键)", "个", 169.0], [0, 6, "X6智能开关(单键)(零火版)", "个", 129.0], [0, 6, "X6智能开关(双键)(零火版)", "个", 139.0], [0, 6, "X6智能开关(三键)(零火版)", "个", 149.0], [0, 6, "X6旋转调光开关", "个", 150.0], [0, 6, "平头熊X6单键开关", "个", 170.0], [0, 6, "平头熊X6双键开关", "个", 180.0], [0, 6, "X6(零火版三键开关)", "个", 190.0], [0, 6, "平头熊X6三键开关", "个", 190.0], [0, 6, "X6(零火版6键场景开关)", "个", 269.0], [0, 7, "绿米Z1(零火单键)", "个", 148.0], [0, 7, "绿米Z1(零火双键)", "个", 168.0], [0, 7, "X1PRO(零火单键)", "个", 179.0], [0, 7, "绿米Z1(零火三键)", "个", 188.0], [0, 7, "X1PRO(零火双键)", "个", 189.0], [0, 7, "X1PRO(零火三键)", "个", 199.0], [0, 7, "绿米Z1(单零火通用四键)", "个", 208.0], [0, 7, "X1PRO(零火四键)", "个", 209.0], [0, 8, "胡桃木智能开关(零火单键)", "个", 129.0], [0, 8, "胡桃木智能开关(零火双键)", "个", 139.0], [0, 8, "胡桃木智能开关(零火三键)", "个", 149.0], [0, 8, "胡桃木智能开关(零火四键)", "个", 159.0], [0, 9, "小米无线智能开关贴", "个", 20.0], [0, 9, "小米无线智能开关贴(场景按键)", "个", 29.9], [0, 9, "无线圆钮开关", "个", 35.0], [0, 9, "无线开关AK(单键)灰色", "个", 40.0], [0, 9, "无线开关双键白色", "个", 45.0], [0, 9, "无线开关(单键)", "个", 45.0], [0, 9, "无线开关", "个", 45.0], [0, 9, "无线开关(单键小米灰)", "个", 45.0], [0, 9, "无线开关AK(双键)灰色", "个", 45.0], [0, 9, "无线开关三键白色", "个", 47.5], [0, 9, "无线智能开关(双键)", "个", 50.0], [0, 9, "无线开关AK(三键)灰色", "个", 50.0], [0, 9, "无线智能开关(三键)", "个", 55.0], [0, 9, "无线开关单键", "个", 55.0], [0, 9, "AK无线开关(单键)(无线开关)", "个", 55.0], [0, 9, "AK无线开关(双键)(无线开关)", "个", 65.0], [0, 9, "无线开关(双键)", "个", 67.0], [0, 9, "窗帘无线开关控制器(开/停/关)", "个", 69.0], [0, 9, "X6无线调光情景开关", "个", 140.0], [0, 10, "肤感玻璃五孔插座", "个", 69.0], [0, 10, "智能插座", "个", 89.0], [1, 0, "智能射灯", "个", 129.0], [1, 0, "轨道射灯(暗装款)10w", "个", 129.0], [1, 0, "(满星思维/征服者)75mm智能调光射灯", "盏", 129.0], [1, 0, "12w射灯", "个", 129.0], [1, 0, "米家智能射灯", "个", 129.0], [1, 0, "易来智能射灯C3(12W-36°)", "个", 129.0], [1, 0, "射灯7w24°", "个", 129.0], [1, 0, "射灯7w36°", "个", 129.0], [1, 0, "射灯9w36°", "个", 129.0], [1, 0, "智能筒灯(射灯60度)", "个", 129.0], [1, 0, "米家智能明装射灯", "个", 136.0], [1, 0, "7w-10w-12w智能射灯", "个", 139.0], [1, 0, "智能射灯(55度)", "个", 149.0], [1, 0, "智能射灯(36度)", "个", 149.0], [1, 0, "明装智能射灯", "个", 149.0], [1, 0, "磁吸轨道射灯", "个", 169.0], [1, 0, "平头熊X6智能射灯8w24°", "个", 169.0], [1, 0, "平头熊X6智能射灯24°", "个", 169.0], [1, 0, "平头熊X6智能射灯36°", "个", 169.0], [1, 0, "平头熊X6智能射灯15°", "个", 169.0], [1, 0, "绿米T3-36°射灯", "个", 169.0], [1, 0, "智能射灯36°12w2700-6500色温调节-0%-100%亮度调节", "个", 169.0], [1, 0, "智能射灯24°12w2700-6500色温调节-0%-100%亮度调节", "个", 169.0], [1, 0, "智能射灯(12w-RA98)", "个", 179.0], [1, 0, "(满星思维/黑骑士)75mm智能调光射灯", "盏", 189.0], [1, 0, "射灯H1", "个", 189.0], [1, 0, "嵌入式轨道射灯20w", "个", 269.0], [1, 1, "7w筒灯", "个", 89.0], [1, 1, "筒灯10w60°", "个", 89.0], [1, 1, "智能筒灯10w", "个", 89.0], [1, 1, "三雄极光12w筒灯", "个", 99.0], [1, 1, "智能筒灯", "个", 99.0], [1, 1, "7w-10w智能筒灯90-95开孔", "个", 109.0], [1, 1, "易来智能筒灯C3(10W-55°)", "个", 129.0], [1, 1, "75开孔智能筒灯", "个", 129.0], [1, 1, "易来智能筒灯S1(10W-90°)", "个", 129.0], [1, 1, "平头熊智能筒灯60°", "个", 149.0], [1, 1, "明装筒灯12w", "个", 159.0], [1, 1, "筒灯H1", "个", 169.0], [1, 2, "灯带接驳配件", "米", 16.0], [1, 2, "柜体灯带", "米", 22.0], [1, 2, "双色温灯带(10米/卷)", "米", 22.0], [1, 2, "智能cob调光灯带", "米", 26.0], [1, 2, "智能双色温全光谱调光灯带19.5", "米", 30.0], [1, 2, "灯带", "米", 35.0], [1, 2, "智能双色温灯带8mm2700-6500色温调节-0%-100%亮度调节", "米", 35.0], [1, 2, "cob灯带-2700-6500k(12w/米)", "米", 35.0], [1, 2, "cob无极调光灯带", "米", 35.0], [1, 2, "智能COB全光谱双色温调光灯带", "米", 35.0], [1, 2, "智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)", "米", 37.0], [1, 2, "裸板双色温智能灯带(", "米", 43.0], [1, 2, "智能COB调光灯带", "米", 45.0], [1, 2, "智能COB灯带(RA97-2700k-6500k色温调节0%-100%亮度调节)", "米", 45.0], [1, 2, "无边线性灯型材(含灯带)", "米", 50.0], [1, 2, "轨道泛光灯(暗装款)10w", "个", 76.0], [1, 2, "轨道格栅灯(暗装款)12w", "个", 119.0], [1, 2, "轨道吊线灯10w", "个", 140.0], [1, 2, "智能灯带", "盘/10米", 189.0], [1, 2, "双色温灯带10米", "10米/卷", 189.0], [1, 2, "三雄极光灯带", "10米/盘", 189.0], [1, 2, "智能调光COB灯带5m一盘", "个", 240.0], [1, 2, "嵌入式格栅灯", "个", 245.0], [1, 2, "智能灯带控制器", "个", 269.0], [1, 2, "双色温cob灯带H1", "盘/5米", 299.0], [1, 2, "床头灯带套餐", "个", 899.0], [1, 3, "磁吸轨道导电接头", "个", 25.0], [1, 3, "磁吸轨道直连模块", "个", 39.0], [1, 3, "嵌入式磁吸直线连接器", "个", 39.0], [1, 3, "明装磁吸轨道", "米", 50.0], [1, 3, "明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)", "米", 80.0], [1, 3, "磁吸轨道", "米", 89.0], [1, 3, "嵌入式磁吸轨道", "米", 90.0], [1, 3, "磁吸轨道格栅灯", "个", 149.0], [1, 3, "12W磁吸防眩格栅灯", "个", 165.0], [1, 3, "磁吸轨道灯(泛光灯)", "个", 179.0], [1, 3, "嵌入式磁吸泛光灯", "30cm", 199.0], [1, 3, "智能磁吸格栅灯H1", "个", 199.0], [1, 3, "磁吸轨道灯(聚光灯)", "个", 239.0], [1, 3, "嵌入式磁吸格栅灯-12头", "30cm", 289.0], [1, 3, "智能磁吸格泛光灯H160cm", "个", 289.0], [1, 4, "无线小圆钮", "个", 20.0], [1, 4, "64G内存卡", "个", 39.0], [1, 4, "弯轨转接头", "个", 60.0], [1, 4, "玻璃胶", "个", 62.5], [1, 4, "筒射灯驱动", "个", 72.0], [1, 4, "智能灯带调光驱动器", "个", 79.0], [1, 4, "U型弯轨费", "个", 80.0], [1, 4, "小米智能摄像头75mm开孔嵌入器及电源", "个", 85.0], [1, 4, "60w低压电源", "个", 109.0], [1, 4, "智能灯带电源120w", "个", 109.0], [1, 4, "100w24v变压器(层板)两线单色温", "个", 125.0], [1, 4, "灯带一体式驱动电源150瓦", "个", 129.0], [1, 4, "智能调光灯带电源200w", "个", 130.0], [1, 4, "灯带调光驱动", "个", 135.0], [1, 4, "卡农Z1", "个", 139.0], [1, 4, "色温灯带智能驱动120w", "个", 149.0], [1, 4, "60w24v杜邦接口变压器", "个", 150.0], [1, 4, "智能灯带电源300w", "个", 169.0], [1, 4, "灯带电源驱动", "个", 169.0], [1, 4, "(满星思维/灯带调光电源驱动器)120w", "个", 169.0], [1, 4, "柜体专用调光驱动(米家)150w", "个", 175.0], [1, 4, "100w低压电源", "个", 179.0], [1, 4, "智能调光一体式电源驱动", "个", 189.0], [1, 4, "灯带一体式电源驱动200w", "个", 189.0], [1, 4, "米家一体式驱动电源", "个", 189.0], [1, 4, "小米BE3600", "个", 219.0], [1, 4, "智能300w电源驱动调光控制器", "个", 225.0], [1, 4, "150w低压电源", "个", 229.0], [1, 4, "灯带一体式驱动电源300瓦", "个", 250.0], [1, 4, "智能灯带电源驱动300w", "个", 265.0], [1, 4, "吊灯调光器", "个", 267.5], [1, 4, "绿米S1E", "个", 269.0], [1, 4, "妙控S1E", "个", 269.0], [1, 4, "小米be5000", "个", 289.0], [1, 4, "AX3000", "个", 289.0], [1, 4, "小米ax3000", "个", 329.0], [1, 4, "磁吸轨道电源H1", "条", 350.0], [1, 4, "嵌入式磁吸在轨电源", "个", 350.0], [1, 4, "X6液晶旋钮主机", "个", 369.0], [1, 4, "中枢无关", "个", 399.0], [1, 4, "AX6000", "个", 439.0], [1, 4, "小米aX6000", "个", 469.0], [1, 4, "BE6500", "个", 499.0], [1, 4, "1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)", "个", 599.0], [1, 4, "xiaomisound", "个", 599.0], [1, 4, "X6液晶旋扭主机", "个", 599.0], [1, 4, "tplink", "个", 1200.0], [1, 4, "小米ax9000", "个", 1299.0], [1, 4, "AX9000", "个", 1299.0], [1, 4, "72英寸16:9天花幕(4k高清纳米白玻纤)", "个", 1799.0], [1, 4, "鹿客S50MPRO", "个", 1849.0], [1, 4, "绿米H100", "个", 2799.0], [1, 4, "鹿客V5MAX", "个", 3049.0], [1, 4, "米家十字门521L超薄嵌入式", "个", 3499.0], [1, 4, "绿米D200", "个", 4398.0], [1, 5, "COB双色温灯带(电视背景墙)4*2", "米", 37.0], [1, 5, "米家智能灯泡e14", "个", 45.0], [1, 5, "E14智能灯泡", "个", 55.0], [1, 5, "电视红外语控器", "米", 69.0], [1, 5, "吸顶灯L1", "个", 219.0], [1, 5, "吸顶灯(卧室)36w", "个", 324.8], [1, 5, "智能天际灯(RA90-3000k-6000k色温调节0%-100%亮度调节)", "套/4m", 425.0], [1, 5, "智能吸顶灯50w", "个", 557.0], [1, 5, "吸顶灯96w", "个", 778.0], [1, 6, "16*16三角灯槽", "米", 8.6], [1, 6, "U型型材", "米", 16.0], [1, 6, "灯槽", "米", 23.0], [1, 6, "灯槽(悬浮吊顶灯槽)", "米", 60.0], [2, 0, "CM窗帘电机(双轨)", "个", 175.0], [2, 0, "CM窗帘电机(单轨)", "个", 175.0], [2, 0, "mesh智能窗帘电机", "个", 189.0], [2, 0, "杜亚静音款mesh智能窗帘电机", "个", 299.0], [2, 0, "窗帘电机", "个", 299.0], [2, 0, "静音窗帘电机", "个", 299.0], [2, 0, "平头熊窗帘电机", "个", 299.0], [2, 0, "(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "个", 299.0], [2, 0, "静音款窗帘电机(双弯轨)", "个", 299.0], [2, 0, "智能窗帘电机S6", "个", 299.0], [2, 0, "窗帘电机(单轨双电机)", "个", 299.0], [2, 0, "杜亚静音款mesh智能窗帘电机(弯轨)", "个", 299.0], [2, 0, "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)", "个", 480.0], [2, 0, "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)", "个", 480.0], [2, 0, "窗帘电机(含3米轨道)", "个", 499.0], [2, 0, "杜亚静音款mesh智能窗帘电机大扭力", "个", 499.0], [2, 0, "杜亚静音款mesh智能窗帘电机(含3米轨道)", "个", 559.0], [2, 0, "25卷帘电机", "个", 599.0], [2, 0, "杜亚35卷帘电机", "个", 699.0], [2, 0, "窗帘电机C2(含3米轨道)(纱帘2.2米)", "个", 1098.0], [2, 1, "1.6静音电动窗帘轨道", "米", 50.0], [2, 1, "杜亚静音款1.6厚窗帘轨道", "米", 60.0], [2, 1, "电机静音导轨/米2.0厚", "米", 80.0], [2, 1, "窗帘轨道(单轨单电机)", "米", 80.0], [2, 1, "窗帘盒", "米", 80.0], [2, 1, "窗帘轨道2.0(静音款)弯轨", "米", 80.0], [2, 1, "窗帘轨道", "米", 80.0], [2, 1, "2.0静音窗帘轨道", "米", 80.0], [2, 1, "窗帘轨道2.0(静音款)直轨", "米", 80.0], [2, 1, "静音窗帘轨道(双轨)", "米", 80.0], [2, 1, "静音窗帘轨道(1单轨)", "米", 80.0], [2, 1, "窗帘轨道(弯轨)", "米", 80.0], [2, 1, "杜亚静音款2.0厚窗帘轨道(弯轨)6.91+0.539", "米", 80.0], [2, 1, "窗帘轨道(双轨)", "米", 89.0], [2, 1, "铝合金窗帘盒", "米", 110.0], [2, 1, "杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4", "米", 125.0], [2, 2, "铝百叶", "平方", 88.0], [2, 2, "百叶帘电机(杜亚可接入米家微调)", "个", 550.0], [2, 2, "木百叶35管状智能电机", "个", 699.0], [2, 3, "卷帘面料", "平方", 80.0], [2, 3, "梦幻帘布料及轨道2.75高", "平方", 135.0], [2, 3, "日夜帘布料1.8*1.7", "平方", 185.0], [2, 4, "电动开窗器", "套/单扇窗户", 859.0], [2, 5, "电动窗帘(双轨卧室5)", "米", 65.0], [2, 5, "电动窗帘(单轨)", "米", 65.0], [2, 5, "2.0静音电机轨道", "米", 80.0], [2, 5, "轨道超过3米按80/米增加", "个", 83.0], [2, 5, "梦幻帘轨道", "个", 120.0], [2, 5, "日夜帘=1.139*1.775", "平方", 195.0], [2, 5, "静音电机", "个", 299.0], [2, 5, "智能窗帘系统", "个", 437.82], [2, 5, "智能窗帘大扭力电机mesh静音款", "个", 450.0], [2, 5, "日夜帘电机(上下两层单电机)", "台", 499.0], [2, 5, "窗帘(电机含3米轨道)", "套", 599.0], [2, 5, "米家隐藏式智能窗帘(含3米轨道)", "个", 599.0], [2, 5, "杜亚静音款mesh日夜帘智能升降电机", "个", 650.0], [2, 5, "智能升降电机", "个", 699.0], [2, 5, "香格里拉帘电机", "个", 720.0], [3, 0, "领普人体感应器", "个", 69.0], [3, 0, "小米人体传感器2S", "个", 69.0], [3, 0, "人体感应器", "个", 89.0], [3, 0, "领普人体存在感应器(,公卫)如干湿分离则需增加", "个", 119.0], [3, 0, "小米人体存在感应器", "个", 121.0], [3, 0, "嵌入式人体存在感应器", "个", 129.0], [3, 0, "领普顶装人体存在传感器", "个", 129.0], [3, 0, "人体存在感应器无线", "个", 129.0], [3, 0, "人体存在传感器", "个", 149.0], [3, 0, "人体存在感应器顶装", "个", 149.0], [3, 0, "领普人体存在感应器", "个", 149.0], [3, 0, "人体存在感应器(干区及)95开孔", "个", 149.0], [3, 0, "毫米波雷达人体存在感应器", "个", 149.0], [3, 0, "绿米人体存在感应器", "个", 369.0], [3, 1, "门磁感应器", "个", 49.0], [3, 1, "水浸传感器", "个", 69.0], [3, 1, "小米门窗感应器", "个", 69.0], [3, 1, "门窗感应器", "个", 89.0], [3, 1, "温湿度感应器", "个", 119.0], [3, 1, "烟雾传感器", "个", 149.0], [3, 1, "小米智能摄像机云台版2k", "个", 199.0], [3, 1, "小米智能摄像机2云台版", "个", 269.0], [3, 2, "水浸报警器", "个", 59.0], [3, 2, "烟雾报警器", "个", 149.0], [3, 2, "霸王锁体", "个", 220.0], [3, 3, "小米智能门锁2", "个", 934.0], [3, 4, "摄像头磁吸底座", "个", 80.0], [3, 4, "小米摄像头3云台版", "个", 199.0], [3, 4, "cw500室外摄像头", "个", 349.0], [3, 4, "摄像头", "个", 599.0], [4, 0, "小米家庭屏pro8上墙支架", "个", 69.9], [4, 0, "家庭屏上墙支架", "个", 85.0], [4, 0, "过道中控屏上墙充电支架", "个", 110.0], [4, 0, "AG智能屏显开关(月壤灰)(零火双键)", "个", 160.0], [4, 0, "AG智能屏显开关(月壤灰)(零火三键)", "个", 176.0], [4, 0, "Xiaomi智能家庭屏Mini", "个", 249.0], [4, 0, "4寸场景屏", "个", 269.0], [4, 0, "4英寸场景屏", "个", 269.0], [4, 0, "智能安防", "个", 269.0], [4, 0, "AK智能屏4英寸", "个", 269.0], [4, 0, "4寸智能场景控制屏", "个", 269.0], [4, 0, "4寸超薄中控屏开关", "个", 348.0], [4, 0, "小米语控屏幕", "个", 349.0], [4, 0, "X6中控屏", "个", 379.0], [4, 0, "小米sound音响", "个", 458.0], [4, 0, "pro8中控屏", "个", 689.0], [4, 0, "小米pro8智能屏", "个", 749.0], [4, 0, "家庭中控大屏pro8", "个", 749.0], [4, 0, "8寸中控屏", "个", 749.0], [4, 0, "智能家庭中控屏", "个", 799.0], [4, 0, "中央集控屏", "个", 799.0], [4, 0, "集悦妙控屏S1PLUS", "个", 1899.0], [4, 1, "小爱音箱play", "个", 99.0], [4, 1, "智能音箱", "个", 269.0], [4, 1, "小米音箱pro", "个", 269.0], [4, 1, "吸顶音箱", "个", 606.64], [4, 2, "多模网关", "个", 229.0], [4, 2, "中枢网关", "个", 349.0], [4, 2, "小米中枢网关", "个", 349.0], [4, 2, "网关M2", "个", 389.0], [4, 2, "绿米M2网关", "个", 399.0], [4, 3, "单频遥控器", "个", 30.0], [4, 3, "双频遥控器", "个", 40.0], [5, 0, "16A挂机控制器", "个", 89.0], [5, 0, "小米空调伴侣", "个", 89.0], [5, 0, "柜机空调控制器", "个", 169.0], [5, 0, "空调伴侣p3", "个", 239.0], [5, 0, "风管机控制器WIFI:350mesh:699", "个", 350.0], [5, 0, "米家温控伴侣", "个", 479.0], [5, 0, "风管机控制", "个", 599.0], [5, 0, "风管机内置控制器", "个", 699.0], [5, 0, "风管机内置控制网关mesh", "个", 699.0], [5, 0, "中央空调控制器", "个", 1299.0], [5, 0, "中央空调VRF(mesh增强版)", "个", 1599.0], [5, 0, "中央空调内置控制网关", "个", 1699.0], [5, 0, "中央空调vfr", "个", 1799.0], [5, 0, "小米空调1.5匹巨省电", "个", 1999.0], [5, 1, "新风内置控制器", "个", 1099.0], [5, 1, "地暖控制器", "个", 1299.0], [5, 2, "小米浴霸pro安装", "个", 60.0], [5, 2, "浴霸转换框", "个", 80.0], [5, 2, "米家智能浴霸转换框", "个", 83.0], [5, 2, "浴霸", "个", 449.0], [5, 2, "米家智能浴霸", "个", 499.0], [5, 2, "小米浴霸pro", "个", 699.0], [5, 2, "米家智能浴霸S1", "个", 899.0], [5, 2, "绿米浴霸T1", "个", 1399.0], [5, 3, "小米空气净化器5s", "个", 1539.0], [5, 3, "米家全能扫拖机器人M30S", "台", 2999.0], [5, 3, "米家全能扫地机器人M30PRO", "台", 4699.0], [5, 4, "米家智能晾衣架1s", "个", 1059.0], [5, 4, "智能晾衣架", "个", 1299.0], [5, 4, "米家智能晾衣架Pro", "个", 1299.0], [5, 5, "米家零热水燃气热水器18L", "个", 2199.0], [5, 5, "小米极光投影仪1S", "台", 5599.0], [6, 0, "WIFI-面板", "个", 25.0], [6, 0, "WIFI8口交换机", "个", 129.0], [6, 0, "WIFIax3000(小米)", "个", 299.0], [6, 0, "WIFI-锐捷4口千兆交换机", "个", 300.0], [6, 0, "米家路由器BE5000", "个", 319.0], [6, 0, "WiFi(小米万兆交换机)", "个", 349.0], [6, 0, "WIFI-ap162e", "个", 349.0], [6, 0, "WiFi-AX6000", "个", 399.0], [6, 0, "WIFIaX6000(小米)", "个", 429.0], [6, 0, "WiFi(有线mesh)(小米)", "个", 439.0], [6, 0, "WiFi", "个", 479.0], [6, 0, "WIFIBE6500", "个", 499.0], [6, 0, "WIFI覆盖", "个", 659.0], [6, 0, "WIFI主路由BE6500PRO", "个", 659.0], [6, 0, "米家路由器BE7000", "个", 799.0], [6, 0, "WiFi-AX9000", "个", 899.0], [6, 0, "小米万兆WiFi", "个", 1499.0], [6, 0, "万兆路由器", "个", 1499.0], [6, 1, "千兆交换机", "个", 129.0], [6, 1, "小米千兆8口交换机", "个", 129.0], [6, 1, "万兆交换机", "个", 349.0], [6, 1, "小米万兆交换机", "个", 349.0], [6, 1, "8口千兆AC交换机", "个", 1050.0], [6, 1, "AC+poe交换机(锐捷10口110w)", "个", 1100.0], [6, 1, "AC一体交换机", "个", 1600.0], [6, 2, "壁挂ap盖板", "个", 10.0], [6, 2, "壁挂ap", "个", 350.0], [6, 2, "WiFi(AX6000mesh组网)", "个", 439.0], [6, 2, "WIFImesh组网BE6500", "个", 449.0], [6, 2, "mesh组网", "个", 499.0], [6, 2, "吸顶ap", "个", 550.0], [6, 2, "WIFImesh组网(主路由)弱电箱", "个", 659.0], [7, 0, "200w低压电源", "米", 279.0], [7, 1, "弱电箱排插头", "个", 26.78], [7, 1, "四位排插", "个", 45.0], [7, 1, "485协议模块", "个", 480.0]], "alias": {"4|0|4寸场景屏": ["4寸中控场景屏", "4寸场景屏", "4寸家庭场景屏", "4寸智能场景屏", "4寸智能家庭场景屏", "4寸智能屏", "智能4寸场景屏", "智能4寸家庭场景屏"], "4|0|家庭屏上墙支架": ["中控屏上墙充电支架", "家庭屏上墙支架"], "4|0|4英寸场景屏": ["4英寸场景屏", "四寸智能场景屏", "四寸智能场景屏(3开关8场景)", "智能家庭场景屏"], "4|0|智能家庭中控屏": ["可移动家庭中控屏", "智能中控屏(门处)", "智能家庭中控屏", "过道中控屏"], "0|7|绿米Z1(零火三键)": ["绿米Z1(单零火通用三键)", "绿米Z1(零火三键)"], "0|7|绿米Z1(零火双键)": ["绿米Z1(单零火通用双键)", "绿米Z1(零火双键)"], "0|7|绿米Z1(零火单键)": ["绿米Z1(单零火通用单键)", "绿米Z1(零火单键)"], "0|1|开关刻字": ["开关刻字", "开关按键刻字", "开关按键刻字+图标", "开关按键刻字及图标"], "1|4|64G内存卡": ["64G内存卡", "场景按钮"], "1|4|玻璃胶": ["弯弧费", "物流费", "玻璃胶"], "2|5|轨道超过3米按80/米增加": ["5孔带Type-C与USB接口", "轨道超过3米按80/米增加", "零线发生器"], "2|5|梦幻帘轨道": ["布帘增加米数", "梦幻帘轨道", "纱帘增加米数"], "2|5|日夜帘=1.139*1.775": ["日夜帘=1.139*1.775", "日夜帘=2.141*1.743"], "4|0|智能安防": ["小爱智能音响Pro", "小爱音响pro(红外版)", "智能安防", "电动球阀", "监控"], "1|4|中枢无关": ["中枢无关", "平开窗推窗器", "斗胆灯", "灯光调试费"], "2|5|香格里拉帘电机": ["智能梦幻帘电机", "电动推窗器包安装0.48*1.158", "电动推窗器包安装0.558*1.158", "苹果HomePodmini", "西餐梦幻帘电机(未含轨道)", "香格里拉帘电机"], "4|0|中央集控屏": ["上墙语音总控屏", "中央集控屏", "中控家庭语音屏", "智能中控语音屏"], "0|9|无线开关三键白色": ["无线开关三键白色", "无线开关(三键)"], "0|9|无线开关双键白色": ["无线开关双键白色", "无线开关(白色双键)"], "0|9|无线开关(单键)": ["无线开关(单键)", "无线智能开关(单键)"], "0|6|平头熊X6三键开关": ["X6智能开关(三键)", "平头熊X6三键开关"], "0|6|平头熊X6双键开关": ["X6智能开关(双键)", "平头熊X6双键开关"], "0|6|平头熊X6单键开关": ["X6智能开关(单键)", "平头熊X6单键开关"], "0|3|AG玻璃(零火四键)": ["AG智能开关(四键零火版)", "AG智能开关(四键)(零火版)", "AG玻璃(零火四键)"], "0|3|AG玻璃(零火三键)": ["AG智能开关(三键零火版)", "AG智能开关(三键)(零火版)", "AG玻璃(零火三键)"], "0|3|AG智能开关(双键零火版)": ["AG智能开关(双键零火版)", "AG智能开关(双键)(零火版)"], "0|3|AG玻璃(零火单键)": ["AG智能开关(单键零火版)", "AG智能开关(单键)(零火版)", "AG玻璃(零火单键)"], "0|4|平头熊AE(单火三键)": ["AE智能开关(三键)(单火版)", "平头熊AE(单火三键)"], "0|4|平头熊AE(单火双键)": ["AE智能开关(双键)(单火版)", "平头熊AE(单火双键)"], "0|4|平头熊AE(单火单键)": ["AE智能开关(单键)(单火版)", "平头熊AE(单火单键)"], "0|10|智能插座": ["智能插座", "智能移动插座"], "5|0|16A挂机控制器": ["16A挂机控制器", "挂机控制器", "挂机空调伴侣", "挂机空调控制器", "挂机空调智能插座", "柜机控制器", "柜机红外控制", "空调伴侣", "空调控制器"], "5|0|柜机空调控制器": ["柜机空调控制器", "风管机控制器安装调试费"], "5|0|风管机控制": ["风管机控制", "风管机控制器", "风管机控制面板"], "5|0|中央空调控制器": ["中央空调vrf", "中央空调控制器"], "5|0|中央空调vfr": ["中央空调vfr", "中央空调控制网关"], "0|1|肤感玻璃(零火四键)": ["玻璃肤感智能开关(四键)(零火版)", "玻璃超薄肤感智能开关(零火四键)", "肤感玻璃智能开关(零火四键)", "肤感玻璃(零火四键)", "肤感玻璃(零火四键)原数量3"], "0|1|肤感玻璃(单火三键)": ["玻璃肤感智能开关(三键)(单火版)", "玻璃超薄肤感智能开关(单火三键)", "肤感玻璃(单火三键)"], "0|1|肤感玻璃(零火三键)": ["玻璃肤感智能开关(三键)(零火版)", "玻璃超薄肤感智能开关带屏显免刻字(零火三键)", "玻璃超薄肤感智能开关(零火三键)", "肤感玻璃智能开关(零火三键)", "肤感玻璃(零火三键)", "肤感玻璃(零火三键)原数量2"], "0|1|肤感玻璃(单火双键)": ["玻璃肤感智能开关(双键)(单火版)", "玻璃超薄肤感智能开关(单火双键)", "肤感玻璃(单火双键)"], "0|1|肤感玻璃(零火双键)": ["玻璃肤感智能开关(双键)(零火版)", "玻璃超薄肤感智能开关(零火双键)", "肤感玻璃智能开关(零火双键)", "肤感玻璃(零火双键)", "肤感玻璃(零火双键)原数量5"], "0|1|肤感玻璃(单火单键)": ["玻璃肤感智能开关(单键)(单火版)", "玻璃超薄肤感智能开关(单火单键)", "肤感玻璃(单火单键)"], "0|1|肤感玻璃(零火单键)": ["玻璃超薄肤感智能开关(零火单键)", "肤感玻璃智能开关(零火单键)", "肤感玻璃(零火单键)", "肤感玻璃(零火单键)原数量4"], "0|2|PC肤感超薄(零火四键)": ["PC肤感智能开关(零火四键)", "PC肤感超薄(零火四键)", "PC超薄肤感智能开关(零火四键)"], "0|2|PC肤感超薄(零火三键)": ["PC肤感智能开关(零火三键)", "PC肤感超薄(零火三键)", "PC超薄肤感智能开关(零火三键)"], "0|2|PC肤感超薄(零火双键)": ["PC肤感智能开关(零火双键)", "PC肤感超薄(零火双键)", "PC超薄肤感智能开关(零火双键)"], "0|2|PC肤感超薄(零火单键)": ["PC肤感智能开关(零火单键)", "PC肤感超薄(零火单键)", "PC超薄肤感智能开关(零火单键)"], "0|10|肤感玻璃五孔插座": ["肤感玻璃七孔插座", "肤感玻璃五孔插座"], "3|2|霸王锁体": ["定制锁舌", "送霸王锁体", "霸王锁体"], "2|5|日夜帘电机(上下两层单电机)": ["房日夜帘电机(上下两层单电机)", "日夜帘电机(上下两层单电机)"], "2|5|静音电机": ["CM定制款静音电机", "静音电机"], "2|3|卷帘面料": ["功能房卷帘布料(长2850高2645)", "卷帘布料1.43*2.9", "卷帘布料1.45*1.93", "卷帘布料1.47*1.93", "卷帘布料(长2645高1730)", "卷帘面料", "小卷帘布料(长2855高2150)", "小卷帘布料(长985高2150)", "过道右房卷帘布料(长2545高1730)", "过道左房卷帘布料(长2215高1725)", "香格里拉帘布料"], "2|3|梦幻帘布料及轨道2.75高": ["卷帘布料", "梦幻帘布料及轨道2.75高"], "2|3|日夜帘布料1.8*1.7": ["日夜帘布料1.8*1.7", "日夜帘布料2.1*1.7", "日夜帘布料(2.2+1.8)*1.7", "(日夜帘布料)1.75*(1.71-0.44)", "(日夜帘布料)2.3*1.5"], "1|4|智能调光一体式电源驱动": ["智能调光一体式电源驱动", "智能调光一体式电源驱动(窗帘灯带)"], "1|4|60w低压电源": ["60w低压电源", "智能60w电源驱动调光控制器", "智能电源驱动控制器60w(", "智能电源驱动控制器60w(天花", "智能电源驱动控制器60w(床头灯带", "智能电源驱动控制器60w(灯带", "智能电源驱动控制器60w(窗帘灯带", "智能电源驱动控制器60w(窗户处", "智能调光灯带60w驱动器+电源器0%-100%亮度调节-2700k-6500k色温调节"], "2|2|铝百叶": ["铝百叶", "铝百叶窗帘(含配件及传动箱)2.245*1.5", "铝百叶窗帘(含配件及传动箱)2.525*1.5"], "2|5|电动窗帘(双轨卧室5)": ["电动窗帘(双轨卧室1)", "电动窗帘(双轨卧室2)", "电动窗帘(双轨卧室3)", "电动窗帘(双轨卧室5)", "电动窗帘(双轨卧室)"], "1|2|裸板双色温智能灯带(": ["裸板双色温智能灯带(", "裸板双色温智能灯带(天花", "裸板双色温智能灯带(床头灯带", "裸板双色温智能灯带(灯带", "裸板双色温智能灯带(窗帘灯带", "裸板双色温智能灯带(窗户处"], "1|2|智能双色温全光谱调光灯带19.5": ["智能双色温全光谱调光灯带19.5", "智能双色温全光谱调光灯带(窗帘灯带)"], "0|1|1.8(一块布单键)布帘": ["1.8(一块布单键)布帘", "(纱帘侧)2.09(一块布单键)布帘"], "0|1|2.38(一块布单键)纱帘": ["2.38(一块布单键)纱帘", "2.565(一块布单键)纱帘", "2.84(一块布单键)纱帘"], "2|0|窗帘电机C2(含3米轨道)(纱帘2.2米)": ["窗帘电机C2(含3米轨道)(布帘2.2米)", "窗帘电机C2(含3米轨道)(布帘3.15米)", "窗帘电机C2(含3米轨道)(布帘4.02米)", "窗帘电机C2(含3米轨道)(纱帘0.95米)", "窗帘电机C2(含3米轨道)(纱帘2.2米)", "窗帘电机C2(含3米轨道)(纱帘2.58米)", "窗帘电机C2(含3米轨道)(纱帘3.15米)", "窗帘电机C2(含3米轨道)(纱帘4.02米)"], "2|0|杜亚35卷帘电机": ["杜亚35卷帘电机", "杜亚卷帘电机"], "2|0|25卷帘电机": ["25卷帘电机", "35卷帘电机", "卷帘电机"], "2|5|窗帘(电机含3米轨道)": ["卧室窗帘(电机含3米轨道)", "窗帘(电机含3米轨道)"], "2|0|窗帘电机(含3米轨道)": ["窗帘电机(含3米轨道)", "窗帘电机(含3米轨道)2.98米", "窗帘电机(含3米轨道)3.5米"], "2|0|(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)": ["(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(储物室)(单直轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(单直轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(卧室2女儿房)(双直轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(双直轨)"], "2|0|(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)": ["(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(卧室一)(双弯轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(双弯轨)", "(绿米窗帘电机C2)(一层)定制静音款电动窗帘电机(客房)(双弯轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(双弯轨)", "(绿米窗帘电机C2)(二层)定制静音款电动窗帘电机(品茶区)(双弯轨)"], "2|0|杜亚静音款mesh智能窗帘电机": ["杜亚智能窗帘电机mesh静音款", "杜亚静音款mesh智能窗帘电机", "杜亚静音款mesh隐藏智能窗帘电机"], "2|0|窗帘电机": ["智能窗帘电机", "电动窗帘电机", "窗帘电机"], "2|0|静音窗帘电机": ["定制款静音窗帘电机", "智能窗帘电机mesh静音款", "窗帘电机(定制静音款)(双轨)", "窗帘电机(静音款)", "静音款mesh窗帘电机(单轨)", "静音款智能窗帘电机", "静音款窗帘电机", "静音款窗帘电机(mesh本地执行版)", "静音窗帘电机", "静音窗帘电机(1单轨)", "静音窗帘电机(2单轨)", "静音窗帘电机(单轨)", "静音窗帘电机(双轨)"], "2|0|(二层)定制静音款电动窗帘电机(卧室3)(双直轨)": ["(一层)定制静音款电动窗帘电机(储物室)(单直轨)", "(一层)定制静音款电动窗帘电机(单直轨)", "(一层)定制静音款电动窗帘电机(卧室2女儿房)(双直轨)", "(二层)定制静音款电动窗帘电机(卧室3)(双直轨)", "(二层)定制静音款电动窗帘电机(双直轨)"], "2|0|静音款窗帘电机(双弯轨)": ["静音款窗帘电机(单弯轨)(布料不超30公斤1个电机)超过要分轨道分电机", "静音款窗帘电机(双弯轨)", "(一层)定制静音款电动窗帘电机(卧室一)(双弯轨)", "(一层)定制静音款电动窗帘电机(双弯轨)", "(一层)定制静音款电动窗帘电机(客房)(双弯轨)", "(二层)定制静音款电动窗帘电机(双弯轨)", "(二层)定制静音款电动窗帘电机(品茶区)(双弯轨)"], "2|1|电机静音导轨/米2.0厚": ["杜亚静音款2.0厚窗帘轨道", "杜亚静音款2.0厚窗帘轨道3.161", "杜亚静音款2.0厚窗帘轨道5.79+0.3", "杜亚静音款2.0厚窗帘轨道(卧室)3.09", "杜亚静音款2.0厚窗帘轨道(大女儿房)3.047", "杜亚静音款2.0厚窗帘轨道(小女儿房)2.267", "杜亚静音款2.0厚窗帘轨道(布帘)2.635", "杜亚静音款2.0厚窗帘轨道(纱帘)1.695+6.3", "杜亚静音款2.0厚窗帘轨道(纱帘)2.635", "杜亚静音款2.0厚窗帘轨道(纱帘)3.5+1.7", "杜亚静音款2.0厚窗帘轨道(纱帘)3.542", "杜亚静音款2.0厚窗帘轨道(超出一米)", "电机静音导轨/米2.0厚"], "2|1|杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4": ["杜亚静音款2.0厚隐藏窗帘轨道(布帘)2.4", "杜亚静音款2.0厚隐藏窗帘轨道(纱帘)2.4"], "2|1|窗帘轨道(单轨单电机)": ["沙发侧窗帘轨道(单轨单电机)", "窗帘轨道(单轨单电机)", "窗帘轨道(单轨双电机)", "茶室窗帘轨道(单轨单电机)"], "2|1|窗帘轨道(双轨)": ["三楼右卧室窗帘轨道(双轨)", "左卧室窗帘轨道(双轨)", "电梯卧室窗帘轨道(双轨)", "窗帘轨道(双轨四电机)", "窗帘轨道(双轨)"], "1|4|智能灯带电源300w": ["智能灯带电源300w", "磁吸轨道智能电源300w"], "1|4|智能300w电源驱动调光控制器": ["智能300w电源驱动调光控制器", "磁吸轨道电源(暗装)300w"], "1|4|智能灯带电源驱动300w": ["一体式智能灯带调光电源驱动300w", "智能灯带电源驱动300w", "智能磁吸轨道电源300w(双磁吸轨道)", "灯带一体式电源驱动300w", "灯带变压器24V300W"], "1|3|磁吸轨道导电接头": ["磁吸轨道导电接头", "磁吸轨道直线导通连接"], "1|3|磁吸轨道": ["磁吸轨道", "磁吸轨道泛光灯"], "1|3|磁吸轨道格栅灯": ["磁吸轨道格栅灯", "磁吸轨道格栅灯30cm(支持2700k-6500k色温调节-0%-100%光度调节)"], "1|3|磁吸轨道灯(泛光灯)": ["磁吸轨道泛光灯60cm(支持2700k-6500k色温调节-0%-100%光度调节)", "磁吸轨道灯(泛光灯)"], "1|4|灯带一体式电源驱动200w": ["智能调光一体式电源驱动器200w2700-6500色温调节-0%-100%亮度调节", "灯带一体式电源驱动200w", "磁吸轨道电源(明装200w)", "磁吸轨道电源(暗装200w)"], "1|0|智能射灯": ["射灯", "智能射灯", "智能射灯(2700-6500可调色温1%-100%可调亮度)", "智能筒射灯", "筒射灯"], "1|0|磁吸轨道射灯": ["智能射灯(RA>95、防眩晕、抗蓝光)", "磁吸轨道吊射灯", "磁吸轨道射灯"], "1|3|嵌入式磁吸轨道": ["嵌入式带边磁吸轨道", "嵌入式磁吸轨道", "磁吸轨道(嵌入式)3米一条"], "2|1|窗帘轨道2.0(静音款)弯轨": ["窗帘轨道2.0(静音款)弯轨", "窗帘轨道2.0(静音款)弯轨短", "窗帘轨道2.0(静音款)弯轨长", "静音款窗帘轨道(单弯轨)", "静音窗帘轨道(含弯轨)", "(一层)定制静音款电动窗帘轨道(卧室一)(双弯轨)", "(一层)定制静音款电动窗帘轨道(双弯轨)", "(一层)定制静音款电动窗帘轨道(客房)(双弯轨)", "(二层)定制静音款电动窗帘轨道(双弯轨)", "(二层)定制静音款电动窗帘轨道(品茶区)(双弯轨)"], "2|1|窗帘轨道": ["电动窗帘轨道", "窗帘轨道", "窗帘轨道(增加0.5米/条)"], "2|1|2.0静音窗帘轨道": ["2.0静音电动窗帘轨道", "2.0静音电动窗帘轨道(超出一米)", "2.0静音窗帘轨道", "CM定制款静音轨道", "定制款静音窗帘轨道2.0", "杜亚静音款2.0后窗帘轨道", "杜亚静音款2.0后窗帘轨道(布帘)=2.448+1.018", "杜亚静音款2.0后窗帘轨道(瑜伽房)=2.174+1.55", "杜亚静音款2.0后窗帘轨道(纱帘)=2.448+1.018", "杜亚静音款2.0后窗帘轨道(纱帘)=3.24+1.15", "杜亚静音窗帘轨道", "杜亚静音窗帘轨道2.965-0.51-0.155", "杜亚静音窗帘轨道3.14-0.95", "静音款智能窗帘轨道", "静音款窗帘轨道", "静音轨道"], "2|1|窗帘轨道2.0(静音款)直轨": ["窗帘轨道2.0(静音款)直轨", "(一层)定制静音款电动窗帘轨道(储物室)(单直轨)", "(一层)定制静音款电动窗帘轨道(单直轨)", "(一层)定制静音款电动窗帘轨道(卧室2女儿房)(双直轨)", "(二层)定制静音款电动窗帘轨道(卧室3)(双直轨)", "(二层)定制静音款电动窗帘轨道(双直轨)"], "2|1|静音窗帘轨道(双轨)": ["窗帘轨道(定制静音款)(双轨)", "静音窗帘轨道(A双轨)", "静音窗帘轨道(双轨)", "静音窗帘轨道(双轨)(长3945宽1500高2705)", "静音窗帘轨道(右卧室双轨布帘)", "静音窗帘轨道(右卧室双轨纱帘)", "静音窗帘轨道(左卧室双轨布帘)", "静音窗帘轨道(左卧室双轨纱帘)"], "2|1|静音窗帘轨道(1单轨)": ["杜亚静音窗帘轨道(单轨)", "静音款电动窗帘轨道(单轨)", "静音窗帘轨道(1单轨)", "静音窗帘轨道(2单轨)", "静音窗帘轨道(单轨布帘)", "静音窗帘轨道(单轨纱帘)", "静音窗帘轨道(单轨)"], "1|3|明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)": ["明装磁吸轨道(实际测量尺寸3.8米、离墙间隔40cm)", "明装磁吸轨道(简餐吧台)(实际测量尺寸3.01米、离墙间隔20cm)"], "1|5|E14智能灯泡": ["E14智能灯泡", "E27智能灯泡"], "1|0|(满星思维/黑骑士)75mm智能调光射灯": ["(满星思维/黑骑士)75mm智能单控调光筒射灯", "(满星思维/黑骑士)75mm智能调光射灯"], "1|0|12w射灯": ["12w射灯", "12w智能射灯(2700k-6500k)"], "1|0|智能射灯(12w-RA98)": ["智能射灯(12w-RA97)", "智能射灯(12w-RA98)"], "1|0|智能射灯(36度)": ["智能射灯(36度)", "智能射灯(过道36度)"], "1|0|米家智能射灯": ["米家智能射灯", "米家智能射灯(门)"], "1|4|筒射灯驱动": ["电源", "筒射灯驱动"], "1|4|灯带一体式驱动电源150瓦": ["一体式驱动电源", "智能灯带驱动", "灯带一体式驱动电源150瓦", "灯带电源器", "灯带驱动", "灯带驱动电源", "驱动", "驱动电源通断器"], "1|4|灯带电源驱动": ["智能橱柜灯带电源", "灯带电源驱动"], "1|4|灯带一体式驱动电源300瓦": ["灯带一体式驱动电源300瓦", "灯带电源+驱动"], "1|4|1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)": ["1.5m智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)", "床底灯带套餐(感应器、铝材灯槽、灯带、电源、驱动器、固定配件)", "智能床底灯带套餐(含电源、驱动器、灯带、灯槽、感应器)", "智能床底灯带(感应器、灯带、铝槽、线材、插头、变压器、驱动器)"], "1|2|灯带接驳配件": ["U型型材(天花灯带1)", "U型型材(洗帘灯带)", "U型型材(立面灯带)", "灯带接驳配件"], "1|2|柜体灯带": ["天花灯带", "柜体灯带", "铝型灯槽10*10mm(灯带)"], "1|2|智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)": ["COB双色温灯带(一楼)2*1", "COB双色温灯带(主人房)4*1", "COB双色温灯带(女儿房)4*1", "COB双色温灯带(楼梯灯带)5*1", "COB双色温灯带(楼梯间)2*1", "COB双色温灯带(环形顶)14*1", "智能双色温cob灯带(2700k-6500k色温调节、0%-100%光度调节)"], "1|2|智能COB调光灯带": ["智能COB调光灯带", "智能调光COB灯带", "智能调光COB灯带(天花灯带1)", "智能调光COB灯带(洗帘灯带)", "智能调光COB灯带(立面灯带)"], "1|2|双色温灯带10米": ["三雄极光双色温灯带", "双色温灯带", "双色温灯带10米", "双色温灯带(2700-6500可调色温1%-100%可调亮度)"], "1|4|柜体专用调光驱动(米家)150w": ["150w24v变压器(层板)两线单色温", "柜体专用调光驱动(米家)150w"], "1|4|150w低压电源": ["150w低压电源", "智能灯带电源驱动150w"], "1|4|100w低压电源": ["100w24v杜邦接口变压器", "100w低压电源", "mesh版智能灯带一体式电源驱动100w", "灯带变压器24V100W"], "1|4|智能灯带电源120w": ["智能灯带电源120w", "米家智能电源120w"], "1|4|色温灯带智能驱动120w": ["一体式智能灯带调光电源驱动120w", "一体式驱动电源(120w)", "智能120w电源驱动调光控制器", "智能调光一体式电源驱动器120w2700-6500色温调节-0%-100%亮度调节", "灯带一体式电源驱动120w", "灯带电源一体驱动(120w)", "米家智能调光驱动120w", "色温灯带智能驱动120w"], "1|5|电视红外语控器": ["电视柜灯带2.97米", "电视红外语控器"], "1|2|cob无极调光灯带": ["cob无极调光灯带", "cob无极调光灯带2.57*4", "cob无极调光灯带("], "1|6|灯槽": ["18.5*43.5双眼皮灯槽", "灯槽"], "1|1|易来智能筒灯C3(10W-55°)": ["易来智能筒灯C3(10W-55°)", "易来智能筒灯C3(10W-55°)(门至过道)"], "1|1|75开孔智能筒灯": ["75开孔智能筒灯", "75开孔智能防水筒灯"], "1|1|7w筒灯": ["7w-10w智能筒灯", "7w筒灯", "筒灯7w无极调光"], "1|1|智能筒灯": ["智能筒灯", "智能筒灯(2700-6500可调色温1%-100%可调亮度)"], "3|0|人体感应器": ["人体感应器", "人体感应器(纽扣电池版)"], "3|0|人体存在传感器": ["人体存在传感器", "人体存在感应器", "人体存在感应器(健身房入门处)", "人体存在感应器(免布线纽扣电池版)", "人体存在感应器(入门处)", "人体存在感应器(卧室过道)有线", "免布线人体存在感应器", "公卫人体存在感应器"], "3|0|人体存在感应器顶装": ["人体存在感应器顶装", "人体存在感应器(顶装75mm)", "人体存在感应器(顶装版)", "人体纯在感应器(顶装版)"], "3|0|领普人体感应器": ["领普人体感应器", "领普人体纯在感应器"], "3|0|嵌入式人体存在感应器": ["人体存在感应器(公卫)(嵌入式75mm开孔)", "人体存在感应器(嵌入式75mm开孔)", "嵌入式人体存在感应器"], "3|0|领普顶装人体存在传感器": ["领普人体纯在感应器(顶装版)", "领普顶装人体存在传感器"], "3|0|人体存在感应器无线": ["人体存在感应器无线", "人体存在感应器(公卫干湿区)无线", "人体存在感应器(干湿区)无线"], "3|0|小米人体传感器2S": ["小米人体传感器2S", "小米人体传感器2s", "小米人体感应器", "小米人体感应器2s(入门灯亮)"], "3|1|门磁感应器": ["温湿度传感器", "磁吸感应器", "门磁感应器"], "3|1|水浸传感器": ["水浸传感器", "门窗传感器"], "3|2|烟雾报警器": ["天然气报警器", "烟雾报警器", "燃气报警器"], "4|0|小米家庭屏pro8上墙支架": ["Xiaomi智能家庭屏Pro8上墙支架", "小米家庭屏pro8上墙支架"], "4|0|小米pro8智能屏": ["Xiaomi智能家庭屏Pro8", "小米pro8家庭屏", "小米pro8智能屏", "小米家庭屏pro8"], "3|1|小米智能摄像机云台版2k": ["小米智能摄像机云台版2k", "小米监控"], "3|1|小米智能摄像机2云台版": ["小米小爱音响pro", "小米智能摄像机2云台版"], "4|0|AK智能屏4英寸": ["AK场景屏", "AK智能场景屏", "AK智能屏", "AK智能屏4英寸", "AK智能屏(4英寸)", "ptxAK场景屏", "平头熊AK智能屏4英寸"], "4|0|4寸智能场景控制屏": ["4寸智能场景控制屏", "超薄4寸智能场景控制屏"], "5|0|风管机内置控制器": ["智能风管机内置控制器", "风管机内置控制器", "风管机内置控制模块", "风管机内置控制网关"], "5|0|风管机内置控制网关mesh": ["智能风管机内置控制器mesh", "风管机内置控制网关mesh"], "4|2|中枢网关": ["中枢网关", "智能网关", "网关"], "4|1|智能音箱": ["小爱智能音箱Pro", "小爱音箱Pro", "小爱音箱pro", "智能音箱"], "4|1|小米音箱pro": ["小米小爱音箱Pro", "小米智能音箱pro", "小米音箱pro"], "5|1|新风内置控制器": ["新风内置控制器", "新风内置控制模块"], "5|2|米家智能浴霸S1": ["小米智能浴霸S1", "米家智能浴霸S1"], "5|2|小米浴霸pro": ["小米浴霸Pro", "小米浴霸pro", "米家智能浴霸Pro", "米家智能浴霸pro"], "6|1|万兆交换机": ["万兆交换机", "交换机"], "6|1|8口千兆AC交换机": ["10口千兆AC交换机", "8口千兆AC交换机"], "6|1|小米千兆8口交换机": ["小米千兆8口交换机", "小米千兆交换机"], "1|4|小米be5000": ["小米BE5000", "小米be5000"], "6|2|mesh组网": ["WIFImesh组网", "mesh组网"], "6|0|小米万兆WiFi": ["WiFi(小米万兆路由器)", "小米万兆WiFi"], "6|0|WIFI8口交换机": ["WIFI8口交换机", "WIFI千兆交换机"], "6|0|WIFI-锐捷4口千兆交换机": ["WIFI-锐捷4口千兆交换机", "WIFI-锐捷8口千兆交换机"], "6|0|WIFI-ap162e": ["WIFI-ap162e", "WIFI万兆交换机", "WIFI交换机"], "6|0|WiFi": ["WIFI子路由", "WiFi", "WiFi子母路由"], "6|0|WIFI覆盖": ["WIFI主路由", "WIFI覆盖"], "6|0|WIFIBE6500": ["WIFIBE6500", "WIFI子路由BE6500"], "6|0|WiFi-AX6000": ["Redmi路由器AX6000", "WiFi-AX6000"], "6|0|米家路由器BE5000": ["WiFi(小米路由器BE5000)", "米家路由器BE5000"], "3|4|小米摄像头3云台版": ["小米摄像头3云台版", "小米摄像头3(云台版)", "小米智能摄像头", "小米智能摄像头2云台版"], "5|3|米家全能扫拖机器人M30S": ["小米全能扫拖机器人m30s", "米家全能扫拖机器人M30S"], "5|4|智能晾衣架": ["智能晾衣架", "电动晾衣架"], "5|4|米家智能晾衣架1s": ["米家智能晾衣架1s", "米家智能晾衣架pro"], "5|4|米家智能晾衣架Pro": ["电动晾衣架(小米1s智能晾衣架)", "米家智能晾衣架Pro"], "2|4|电动开窗器": ["太阳能开窗器", "电动开窗器", "电动开窗器(外推)(锂电池款含雨水感应器)", "电动开窗器(锂电池款含雨水感应器)"]}};
438
+ /* ===== 自动升级检查(版本 1.1.0):远端版本更新则跳转最新版,任何失败静默忽略 ===== */
439
+ try { (function () {
440
+ var MYVER = '1.1.0';
441
+ function _cmp(a, b) { try { var x = String(a).split('.'), y = String(b).split('.'), n = Math.max(x.length, y.length); for (var i = 0; i < n; i++) { var p = parseInt(x[i] || '0', 10) || 0, q = parseInt(y[i] || '0', 10) || 0; if (p > q) return 1; if (p < q) return -1; } return 0; } catch (e) { return 0; } }
442
+ function _go(v) { try { location.replace('https://unpkg.com/shulan-quote@' + v + '/index.html'); } catch (e) { } }
443
+ try {
444
+ if (typeof fetch !== 'function') return;
445
+ fetch('https://unpkg.com/shulan-quote/package.json?t=' + Date.now(), { cache: 'no-store' }).then(function (r) { return r && r.ok ? r.json() : null; }).then(function (j) { try { if (!j || !j.version) return; var rv = String(j.version); if (rv !== MYVER && _cmp(rv, MYVER) > 0) _go(rv); } catch (e) { } }).catch(function () { });
446
+ } catch (e) { }
447
+ })(); } catch (e) { }
448
+ /* ================= 树懒智汇家报价系统 v7 ================= */
449
+ var RAW = DATA;
450
+ var REV = 'slzj-v7-480';
451
+ var K_DB = 'slzj_db_v7', K_SESS = 'slzj_sess_v7', K_HIST = 'slzj_hist_v7';
452
+ var K_CLOUD = 'slzj_cloud_v1', K_DBTS = 'slzj_dbts_v1';
453
+ /* 内置云端配置:填好后,任何人打开本页面都会自动连上同一份产品库,无需手动设置 */
454
+ var CLOUD_DEFAULT = { url: '', key: '', room: '' };
455
+
456
+ /* ---------- 基础工具 ---------- */
457
+ function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]; }); }
458
+ function money(n){ n = Math.round((+n||0)*100)/100; return '¥' + n.toLocaleString('zh-CN',{minimumFractionDigits:0,maximumFractionDigits:2}); }
459
+ function p2(n){ return n<10 ? '0'+n : ''+n; }
460
+ function stamp(){ var d=new Date(); return d.getFullYear()+p2(d.getMonth()+1)+p2(d.getDate()); }
461
+ function toast(msg){ var t=document.getElementById('toast'); t.textContent=msg; t.classList.add('on'); clearTimeout(t._h); t._h=setTimeout(function(){ t.classList.remove('on'); }, 1900); }
462
+ function $(id){ return document.getElementById(id); }
463
+ function clone(o){ return JSON.parse(JSON.stringify(o)); }
464
+
465
+ /* ---------- 数据层 ---------- */
466
+ function aliasIndex(){
467
+ var m = {}, a = RAW.alias || {}, k, i1, i2;
468
+ for(k in a){
469
+ i1 = k.indexOf('|'); i2 = k.indexOf('|', i1+1);
470
+ if(i1 < 0 || i2 < 0) continue;
471
+ m[(+k.slice(0,i1)) + '|' + (+k.slice(i1+1,i2)) + '|' + k.slice(i2+1)] = a[k];
472
+ }
473
+ return m;
474
+ }
475
+ function buildBase(){
476
+ var am = aliasIndex(), items = [], cats = [], i, it;
477
+ for(i=0; i<RAW.items.length; i++){
478
+ it = RAW.items[i];
479
+ items.push({
480
+ id: 'b'+i, ci: it[0], si: it[1], name: it[2], unit: it[3], price: it[4],
481
+ alias: am[it[0]+'|'+it[1]+'|'+it[2]] || []
482
+ });
483
+ }
484
+ for(i=0; i<RAW.cats.length; i++) cats.push({ n: RAW.cats[i].n, subs: RAW.cats[i].subs.slice() });
485
+ return { rev: REV, cats: cats, items: items, seq: 0 };
486
+ }
487
+ var DB = (function(){
488
+ try{
489
+ var raw = localStorage.getItem(K_DB);
490
+ if(raw){
491
+ var d = JSON.parse(raw);
492
+ if(d && d.rev === REV && d.cats && d.cats.length && d.items && d.items.length) return d;
493
+ }
494
+ }catch(e){}
495
+ return buildBase();
496
+ })();
497
+ function saveDB(){
498
+ try{
499
+ localStorage.setItem(K_DB, JSON.stringify(DB));
500
+ localStorage.setItem(K_DBTS, String(Date.now()));
501
+ }
502
+ catch(e){ toast('本机保存失败:' + e.message); }
503
+ if(!cloudSkip) cloudPush();
504
+ }
505
+
506
+ /* ---------- 排序:单键→双键→三键→四键 …… ---------- */
507
+ var CN = {'一':1,'二':2,'两':2,'双':2,'单':1,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10};
508
+ function keyNum(n){
509
+ var m = n.match(/([一二两双单三四五六七八九十])\s*键/);
510
+ if(m) return CN[m[1]] || 99;
511
+ m = n.match(/([1-9]|1[0-2])\s*键/);
512
+ if(m) return +m[1];
513
+ return 99;
514
+ }
515
+ function fireKind(n){
516
+ if(/零火/.test(n)) return 2;
517
+ if(/单火/.test(n)) return 1;
518
+ return 0;
519
+ }
520
+ function sortKey(a, b){
521
+ if(a.si !== b.si) return a.si - b.si;
522
+ var ka = keyNum(a.name), kb = keyNum(b.name);
523
+ if(ka !== kb) return ka - kb;
524
+ var fa = fireKind(a.name), fb = fireKind(b.name);
525
+ if(fa !== fb) return fa - fb;
526
+ if(a.price !== b.price) return a.price - b.price;
527
+ return String(a.name).localeCompare(String(b.name), 'zh-CN');
528
+ }
529
+
530
+ /* ---------- 状态 ---------- */
531
+ var state = { ci:0, si:-1, kw:'', bill:[], fee:25, editing:null };
532
+ function saveSess(){
533
+ try{ localStorage.setItem(K_SESS, JSON.stringify({bill:state.bill, fee:state.fee, editing:state.editing})); }catch(e){}
534
+ }
535
+ function loadSess(){
536
+ try{
537
+ var d = JSON.parse(localStorage.getItem(K_SESS) || '{}');
538
+ if(d.bill && d.bill.length) state.bill = d.bill;
539
+ if(typeof d.fee === 'number') state.fee = d.fee;
540
+ if(d.editing && d.editing.id) state.editing = d.editing;
541
+ }catch(e){}
542
+ /* 兼容旧版明细结构 */
543
+ var out = [];
544
+ state.bill.forEach(function(b){
545
+ if(b && b.name && b.unit !== undefined){ out.push(b); }
546
+ });
547
+ state.bill = out;
548
+ }
549
+
550
+ /* ---------- 渲染 ---------- */
551
+ function itemsIn(ci, si){
552
+ var r = [];
553
+ DB.items.forEach(function(x){ if(x.ci === ci && (si < 0 || x.si === si)) r.push(x); });
554
+ return r;
555
+ }
556
+ function renderNav(){
557
+ var h = '<div class="nav-t">板块</div>', cnt;
558
+ DB.cats.forEach(function(c, i){
559
+ cnt = 0;
560
+ DB.items.forEach(function(x){ if(x.ci === i) cnt++; });
561
+ h += '<div class="navi' + (state.ci === i ? ' on' : '') + '" data-ci="' + i + '">'
562
+ + '<span class="idx">' + (i+1) + '</span><span class="n">' + esc(c.n) + '</span><span class="c">' + cnt + '</span></div>';
563
+ });
564
+ $('nav').innerHTML = h;
565
+ }
566
+ function renderStage(){
567
+ if(!DB.cats.length){ $('catName').textContent = '暂无类目'; $('catMeta').textContent = ''; $('chips').innerHTML = ''; $('plist').innerHTML = '<div class="empty">产品库为空,点右上角「产品库」新增类目和产品</div>'; return; }
568
+ if(state.ci >= DB.cats.length) state.ci = 0;
569
+ var cat = DB.cats[state.ci], subs = {}, total = 0, j;
570
+ DB.items.forEach(function(x){ if(x.ci === state.ci){ subs[x.si] = (subs[x.si]||0) + 1; total++; } });
571
+ $('catName').textContent = cat.n;
572
+ $('catMeta').textContent = total + ' 款产品 · ' + cat.subs.length + ' 个子类';
573
+ var ch = '<div class="chip' + (state.si < 0 ? ' on' : '') + '" data-si="-1">全部<span class="cn">' + total + '</span></div>';
574
+ cat.subs.forEach(function(s, k){
575
+ if(!subs[k]) return;
576
+ ch += '<div class="chip' + (state.si === k ? ' on' : '') + '" data-si="' + k + '">' + esc(s) + '<span class="cn">' + subs[k] + '</span></div>';
577
+ });
578
+ $('chips').innerHTML = ch;
579
+ renderList();
580
+ }
581
+ function renderList(){
582
+ var kw = String(state.kw||'').trim().toLowerCase(), list = [], html = '', i, x, ok, al, k;
583
+ if(kw){
584
+ for(i=0; i<DB.items.length; i++){
585
+ x = DB.items[i];
586
+ ok = String(x.name).toLowerCase().indexOf(kw) >= 0;
587
+ if(!ok && x.alias){ for(k=0; k<x.alias.length; k++){ if(String(x.alias[k]).toLowerCase().indexOf(kw) >= 0){ ok = true; break; } } }
588
+ if(ok) list.push(x);
589
+ }
590
+ }else{
591
+ list = itemsIn(state.ci, state.si);
592
+ }
593
+ list.sort(sortKey);
594
+ if(!list.length){
595
+ html = '<div class="empty">没有匹配的产品,换个关键词试试<br>或到「产品库」里新增</div>';
596
+ }else{
597
+ var added = {};
598
+ state.bill.forEach(function(b){ added[b.pid] = 1; });
599
+ for(i=0; i<list.length; i++){
600
+ x = list[i];
601
+ al = x.alias && x.alias.length ? x.alias : null;
602
+ html += '<div class="pcard' + (added[x.id] ? ' added' : '') + '" data-id="' + x.id + '"'
603
+ + (al ? ' title="已合并叫法:' + esc(al.join(' / ')) + '"' : '') + '>'
604
+ + '<button class="pdel" data-pdel="' + x.id + '" title="从产品库删除">&times;</button>'
605
+ + (al ? '<span class="alias">合' + al.length + '</span>' : '')
606
+ + '<div class="pn">' + esc(x.name) + '</div>'
607
+ + '<div class="pm"><div class="pp">' + money(x.price) + '<small>/' + esc(x.unit) + '</small></div>'
608
+ + '<div class="add">+</div></div></div>';
609
+ }
610
+ }
611
+ $('plist').innerHTML = html;
612
+ }
613
+ function renderBill(){
614
+ var body = $('billbody');
615
+ if(!state.bill.length){
616
+ body.innerHTML = '<div class="bempty">左侧选板块,点产品卡加入报价<br>数量和单价都可以再改</div>';
617
+ }else{
618
+ var groups = {}, order = [], h = '';
619
+ state.bill.forEach(function(b, i){
620
+ var key = b.ci + '_' + b.si;
621
+ if(!groups[key]){ groups[key] = {cat:b.cat, sub:b.sub, rows:[]}; order.push(key); }
622
+ groups[key].rows.push({b:b, i:i});
623
+ });
624
+ order.forEach(function(key){
625
+ var g = groups[key], sum = 0;
626
+ g.rows.forEach(function(r){ sum += r.b.qty * r.b.price; });
627
+ h += '<div class="bgrp" data-g="' + key + '"><div class="bgrp-h"><i></i>'
628
+ + esc(g.cat) + (g.sub ? ' · ' + esc(g.sub) : '')
629
+ + '<em class="gsum">' + money(sum) + '</em></div>';
630
+ g.rows.forEach(function(r){
631
+ h += '<div class="row" data-i="' + r.i + '">'
632
+ + '<button class="delbtn" data-del="' + r.i + '">&times;</button>'
633
+ + '<div class="rn">' + esc(r.b.name) + '</div>'
634
+ + '<div class="calc">'
635
+ + '<span class="x">¥</span><input class="ip" type="number" min="0" step="1" value="' + r.b.price + '" data-price="' + r.i + '">'
636
+ + '<span class="x">&times;</span><input class="iq" type="number" min="0" step="1" value="' + r.b.qty + '" data-qty="' + r.i + '">'
637
+ + '<span class="x">' + esc(r.b.unit) + '</span>'
638
+ + '<span class="amt">' + money(r.b.qty * r.b.price) + '</span>'
639
+ + '</div></div>';
640
+ });
641
+ h += '</div>';
642
+ });
643
+ body.innerHTML = h;
644
+ }
645
+ updateSums();
646
+ saveSess();
647
+ }
648
+ function updateSums(){
649
+ var sub = 0, qty = 0;
650
+ state.bill.forEach(function(b, i){
651
+ var s = b.qty * b.price; sub += s; qty += b.qty;
652
+ var a = document.querySelector('.row[data-i="' + i + '"] .amt');
653
+ if(a) a.textContent = money(s);
654
+ });
655
+ document.querySelectorAll('.bgrp').forEach(function(el){
656
+ var key = el.dataset.g, sum = 0;
657
+ state.bill.forEach(function(b){ if(b.ci + '_' + b.si === key) sum += b.qty * b.price; });
658
+ var em = el.querySelector('.gsum');
659
+ if(em) em.textContent = money(sum);
660
+ });
661
+ var feeAmt = sub * state.fee / 100;
662
+ $('subAmt').textContent = money(sub);
663
+ $('feeAmt').textContent = money(feeAmt);
664
+ $('totAmt').textContent = money(sub + feeAmt);
665
+ $('feeTag').textContent = state.fee + '%';
666
+ $('qtyTag').textContent = qty ? ('共 ' + qty + ' 件') : '';
667
+ $('cnt').textContent = state.bill.length ? (state.bill.length + ' 条 / ' + qty + ' 件') : '';
668
+ }
669
+ function renderEditBar(){
670
+ var bar = $('editbar'), eb = $('ebSave'), en = $('ebNew'), ex = $('ebExit');
671
+ if(state.editing && state.editing.id){
672
+ bar.classList.add('on');
673
+ var nm = document.createElement('span');
674
+ $('ebText').textContent = '正在编辑:' + state.editing.name;
675
+ eb.style.display = ''; en.style.display = ''; ex.style.display = '';
676
+ }else{
677
+ bar.classList.remove('on');
678
+ }
679
+ }
680
+ function renderAll(){
681
+ renderNav(); renderStage(); renderBill(); renderEditBar();
682
+ $('stat').textContent = ' · ' + DB.items.length + ' 款产品 · ' + DB.cats.length + ' 个类目';
683
+ }
684
+
685
+ /* ---------- 加购 ---------- */
686
+ function addProduct(it){
687
+ var line = null;
688
+ state.bill.forEach(function(b){ if(b.pid === it.id) line = b; });
689
+ if(line){ line.qty += 1; }
690
+ else{
691
+ state.bill.push({
692
+ pid: it.id, ci: it.ci, si: it.si,
693
+ cat: DB.cats[it.ci] ? DB.cats[it.ci].n : '',
694
+ sub: (DB.cats[it.ci] && DB.cats[it.ci].subs[it.si]) || '',
695
+ name: it.name, unit: it.unit, price: it.price, qty: 1
696
+ });
697
+ }
698
+ renderBill();
699
+ var card = document.querySelector('.pcard[data-id="' + it.id + '"]');
700
+ if(card) card.classList.add('added');
701
+ }
702
+
703
+ /* ---------- 历史报价 ---------- */
704
+ function histAll(){
705
+ try{
706
+ var a = JSON.parse(localStorage.getItem(K_HIST) || '[]');
707
+ return Object.prototype.toString.call(a) === '[object Array]' ? a : [];
708
+ }catch(e){ return []; }
709
+ }
710
+ function histPut(list){
711
+ try{ localStorage.setItem(K_HIST, JSON.stringify(list)); }
712
+ catch(e){ toast('历史保存失败:' + e.message); }
713
+ }
714
+ function quoteTotal(rows, fee){
715
+ var sub = 0;
716
+ (rows||[]).forEach(function(r){ sub += r.qty * r.price; });
717
+ return sub * (1 + (fee||0)/100);
718
+ }
719
+ function renderHist(){
720
+ var list = histAll(), box = $('histBody'), h = '';
721
+ if(!list.length){
722
+ box.innerHTML = '<div class="hempty">还没有保存过报价单<br>在右侧明细里点「保存报价单」即可存档,随时载入修改</div>';
723
+ return;
724
+ }
725
+ list.forEach(function(q){
726
+ h += '<div class="hitem">'
727
+ + '<div class="hi"><div class="hn">' + esc(q.name) + '</div>'
728
+ + '<div class="hm">' + esc(q.date) + ' · ' + (q.rows ? q.rows.length : 0) + ' 条 · 服务费 ' + (q.fee||0) + '%</div></div>'
729
+ + '<div class="hs">' + money(quoteTotal(q.rows, q.fee)) + '</div>'
730
+ + '<div class="ha">'
731
+ + '<button class="mini primary" data-open="' + q.id + '">载入</button>'
732
+ + '<button class="mini" data-rename="' + q.id + '">改名</button>'
733
+ + '<button class="mini danger" data-delq="' + q.id + '">删除</button>'
734
+ + '</div></div>';
735
+ });
736
+ box.innerHTML = h;
737
+ }
738
+ function saveQuote(asNew){
739
+ if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
740
+ var list = histAll(), rec = null, i;
741
+ if(state.editing && !asNew){
742
+ for(i=0; i<list.length; i++) if(list[i].id === state.editing.id) rec = list[i];
743
+ }
744
+ var nm;
745
+ if(rec){ nm = rec.name; }
746
+ else{
747
+ var def = state.editing ? (state.editing.name + ' 副本') : ('报价单 ' + stamp());
748
+ var v = prompt('报价单名称(如:张先生-三房两厅)', def);
749
+ if(v === null) return;
750
+ nm = String(v || '').trim() || def;
751
+ }
752
+ var data = {
753
+ id: rec ? rec.id : ('q' + Date.now()),
754
+ name: nm,
755
+ date: new Date().toLocaleString('zh-CN'),
756
+ fee: state.fee,
757
+ rows: clone(state.bill),
758
+ updated: Date.now()
759
+ };
760
+ if(rec){
761
+ for(i=0; i<list.length; i++) if(list[i].id === rec.id) list[i] = data;
762
+ }else{
763
+ list.unshift(data);
764
+ }
765
+ histPut(list);
766
+ state.editing = { id: data.id, name: data.name };
767
+ renderEditBar();
768
+ saveSess();
769
+ renderHist();
770
+ toast(rec ? ('已更新:' + nm) : ('已保存:' + nm));
771
+ }
772
+ function openQuote(id){
773
+ var list = histAll(), rec = null;
774
+ list.forEach(function(q){ if(q.id === id) rec = q; });
775
+ if(!rec) return;
776
+ if(state.bill.length && !confirm('当前明细会被「' + rec.name + '」覆盖,继续?')) return;
777
+ state.bill = clone(rec.rows || []);
778
+ state.fee = rec.fee || 0;
779
+ state.editing = { id: rec.id, name: rec.name };
780
+ $('fee').value = state.fee;
781
+ if(state.bill.length && DB.cats[state.bill[0].ci]){ state.ci = state.bill[0].ci; state.si = -1; }
782
+ renderAll();
783
+ closeModal('mHist');
784
+ toast('已载入:' + rec.name);
785
+ }
786
+
787
+ /* ---------- 产品库管理 ---------- */
788
+ var mgr = { ci: 0, si: -1, kw: '' };
789
+ function openMgr(){
790
+ mgr.ci = state.ci; mgr.si = state.si >= 0 ? state.si : 0; mgr.kw = '';
791
+ $('mgrKw').value = '';
792
+ $('addRow').style.display = 'none';
793
+ renderMgr();
794
+ show('mMgr');
795
+ }
796
+ function renderMgr(){
797
+ if(mgr.ci >= DB.cats.length) mgr.ci = 0;
798
+ var cat = DB.cats[mgr.ci];
799
+ if(cat && (mgr.si >= cat.subs.length)) mgr.si = cat.subs.length ? 0 : -1;
800
+ renderCatList(); renderSubList(); renderItemRows();
801
+ $('mgrTip').textContent = '共 ' + DB.items.length + ' 款产品 / ' + DB.cats.length + ' 个类目 · 改动自动保存';
802
+ }
803
+ function renderCatList(){
804
+ var h = '';
805
+ DB.cats.forEach(function(c, i){
806
+ var n = 0;
807
+ DB.items.forEach(function(x){ if(x.ci === i) n++; });
808
+ h += '<div class="mli' + (i === mgr.ci ? ' on' : '') + '" data-ci="' + i + '">'
809
+ + '<span class="nm">' + esc(c.n) + '</span><span class="num">' + n + '</span>'
810
+ + '<button class="x" data-rencat="' + i + '" title="重命名">✎</button>'
811
+ + '<button class="x" data-delcat="' + i + '" title="删除类目">&times;</button></div>';
812
+ });
813
+ if(!DB.cats.length) h = '<div class="hempty" style="padding:20px 8px">暂无类目</div>';
814
+ $('catList').innerHTML = h;
815
+ }
816
+ function renderSubList(){
817
+ var cat = DB.cats[mgr.ci], h = '';
818
+ $('subTitle').textContent = '子类' + (cat ? ':' + cat.n : '');
819
+ if(cat){
820
+ cat.subs.forEach(function(s, j){
821
+ var n = 0;
822
+ DB.items.forEach(function(x){ if(x.ci === mgr.ci && x.si === j) n++; });
823
+ h += '<div class="mli' + (j === mgr.si ? ' on' : '') + '" data-si="' + j + '">'
824
+ + '<span class="nm">' + esc(s) + '</span><span class="num">' + n + '</span>'
825
+ + '<button class="x" data-rensub="' + j + '" title="重命名">✎</button>'
826
+ + '<button class="x" data-delsub="' + j + '" title="删除子类">&times;</button></div>';
827
+ });
828
+ }
829
+ if(!h) h = '<div class="hempty" style="padding:20px 8px">暂无子类</div>';
830
+ $('subList').innerHTML = h;
831
+ }
832
+ function renderItemRows(){
833
+ var kw = mgr.kw.trim().toLowerCase(), list = [], h = '';
834
+ DB.items.forEach(function(x){
835
+ if(x.ci !== mgr.ci) return;
836
+ if(mgr.si >= 0 && x.si !== mgr.si) return;
837
+ if(kw && String(x.name).toLowerCase().indexOf(kw) < 0) return;
838
+ list.push(x);
839
+ });
840
+ list.sort(sortKey);
841
+ $('itemTitle').textContent = '产品' + (list.length ? '(' + list.length + ')' : '');
842
+ list.slice(0, 500).forEach(function(x){
843
+ h += '<div class="mtr" data-id="' + x.id + '">'
844
+ + '<input class="nm" value="' + esc(x.name) + '" data-f="name">'
845
+ + '<input class="un" value="' + esc(x.unit) + '" data-f="unit">'
846
+ + '<input class="pr" type="number" min="0" step="1" value="' + x.price + '" data-f="price">'
847
+ + '<button class="x" data-delitem="' + x.id + '" title="删除产品">&times;</button>'
848
+ + '</div>';
849
+ });
850
+ if(!h) h = '<div class="hempty" style="padding:24px 8px">这里还没有产品,点「+ 新增产品」添加</div>';
851
+ $('itemRows').innerHTML = h;
852
+ }
853
+ function delCat(i){
854
+ var cat = DB.cats[i], cnt = 0;
855
+ if(!cat) return;
856
+ DB.items.forEach(function(x){ if(x.ci === i) cnt++; });
857
+ if(!confirm('删除类目「' + cat.n + '」?\n其中 ' + cnt + ' 款产品会一并删除,且不可恢复。')) return;
858
+ DB.cats.splice(i, 1);
859
+ DB.items = DB.items.filter(function(x){ return x.ci !== i; });
860
+ DB.items.forEach(function(x){ if(x.ci > i) x.ci--; });
861
+ if(mgr.ci >= DB.cats.length) mgr.ci = Math.max(0, DB.cats.length - 1);
862
+ mgr.si = 0;
863
+ if(state.ci >= DB.cats.length) state.ci = 0;
864
+ saveDB(); renderMgr(); renderAll();
865
+ toast('已删除类目');
866
+ }
867
+ function delSub(i, j){
868
+ var cat = DB.cats[i];
869
+ if(!cat) return;
870
+ var cnt = 0;
871
+ DB.items.forEach(function(x){ if(x.ci === i && x.si === j) cnt++; });
872
+ if(!confirm('删除子类「' + cat.subs[j] + '」?\n其中 ' + cnt + ' 款产品会一并删除,且不可恢复。')) return;
873
+ cat.subs.splice(j, 1);
874
+ DB.items = DB.items.filter(function(x){ return !(x.ci === i && x.si === j); });
875
+ DB.items.forEach(function(x){ if(x.ci === i && x.si > j) x.si--; });
876
+ if(mgr.si >= cat.subs.length) mgr.si = Math.max(0, cat.subs.length - 1);
877
+ saveDB(); renderMgr(); renderAll();
878
+ toast('已删除子类');
879
+ }
880
+
881
+ /* ---------- 导出 ---------- */
882
+ var isWX = /MicroMessenger/i.test(navigator.userAgent);
883
+ function safeName(s){ return String(s || '报价单').replace(/[\\/:*?"<>|\r\n]/g, '_').slice(0, 60); }
884
+ function dl(blob, filename){
885
+ var ok = false;
886
+ try{
887
+ if(navigator.msSaveOrOpenBlob){ navigator.msSaveOrOpenBlob(blob, filename); ok = true; }
888
+ }catch(e){}
889
+ if(!ok){
890
+ var url = URL.createObjectURL(blob);
891
+ var a = document.createElement('a');
892
+ a.href = url;
893
+ a.download = filename;
894
+ a.rel = 'noopener';
895
+ a.style.cssText = 'position:fixed;left:-9999px;top:0;opacity:0';
896
+ document.body.appendChild(a);
897
+ try{
898
+ if(typeof a.click === 'function'){ a.click(); }
899
+ else{
900
+ var ev = document.createEvent('MouseEvents');
901
+ ev.initEvent('click', true, true);
902
+ a.dispatchEvent(ev);
903
+ }
904
+ ok = true;
905
+ }catch(e){ ok = false; }
906
+ setTimeout(function(){
907
+ try{ document.body.removeChild(a); }catch(e){}
908
+ try{ URL.revokeObjectURL(url); }catch(e){}
909
+ }, 2500);
910
+ }
911
+ return ok;
912
+ }
913
+ function quoteTitle(){
914
+ return state.editing && state.editing.name ? state.editing.name : ('智能家居报价单_' + stamp());
915
+ }
916
+ function billLines(){
917
+ var rows = state.bill, out = [], lastKey = '', sub = 0, i, b, key;
918
+ for(i=0; i<rows.length; i++){
919
+ b = rows[i]; key = b.ci + '_' + b.si;
920
+ if(key !== lastKey){ out.push({ type:'grp', text: b.cat + (b.sub ? ' · ' + b.sub : '') }); lastKey = key; }
921
+ out.push({ type:'row', b:b });
922
+ sub += b.qty * b.price;
923
+ }
924
+ return { lines: out, sub: sub };
925
+ }
926
+ function exportWord(){
927
+ if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
928
+ var data = billLines(), sub = data.sub, feeAmt = sub * state.fee / 100;
929
+ var title = quoteTitle();
930
+ var d = new Date(), ds = d.getFullYear() + '-' + p2(d.getMonth()+1) + '-' + p2(d.getDate());
931
+ var h = [];
932
+ h.push('<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="utf-8"><title>' + esc(title) + '</title>');
933
+ h.push('<style>@page{size:21cm 29.7cm;margin:1.6cm 1.5cm}body{font-family:"宋体",SimSun,serif;font-size:10.5pt;color:#000}');
934
+ h.push('h1{font-family:"微软雅黑";font-size:17pt;text-align:center;margin:0 0 4pt;letter-spacing:2pt}');
935
+ h.push('.sub{text-align:center;font-size:9pt;color:#555;margin-bottom:12pt}');
936
+ h.push('table{border-collapse:collapse;width:100%;margin-bottom:10pt}');
937
+ h.push('th,td{border:1px solid #444;padding:4pt 5pt;font-size:9.5pt;vertical-align:middle}');
938
+ h.push('th{background:#e9eff5;font-weight:bold;text-align:center}');
939
+ h.push('.c{text-align:center}.r{text-align:right}');
940
+ h.push('.grp td{background:#f4f7fa;font-weight:bold}');
941
+ h.push('.sub2 td{background:#f7fbf9;font-weight:bold}');
942
+ h.push('.tot td{background:#eef7f3;font-weight:bold;font-size:10.5pt}');
943
+ h.push('.note{font-size:9pt;color:#444;line-height:1.9}');
944
+ h.push('</style></head><body>');
945
+ h.push('<h1>智能家居配置报价单</h1>');
946
+ h.push('<div class="sub">' + esc(title) + ' | 报价日期:' + ds + ' | 服务费率:' + state.fee + '%</div>');
947
+ h.push('<table><tr><th style="width:34pt">序号</th><th>产品名称</th><th style="width:34pt">单位</th><th style="width:56pt">单价(元)</th><th style="width:40pt">数量</th><th style="width:66pt">小计(元)</th></tr>');
948
+ var n = 0, cur = '', gsum = 0, gcount = 0, rows = state.bill;
949
+ function flushGroup(){
950
+ if(gcount > 1){
951
+ h.push('<tr class="sub2"><td colspan="5" class="r">小计</td><td class="r">' + (Math.round(gsum*100)/100).toFixed(2) + '</td></tr>');
952
+ }
953
+ gsum = 0; gcount = 0;
954
+ }
955
+ for(var i=0; i<rows.length; i++){
956
+ var b = rows[i], key = b.ci + '_' + b.si;
957
+ if(key !== cur){
958
+ flushGroup();
959
+ cur = key;
960
+ h.push('<tr class="grp"><td colspan="6">' + esc(b.cat) + (b.sub ? ' · ' + esc(b.sub) : '') + '</td></tr>');
961
+ }
962
+ n++;
963
+ var amt = b.qty * b.price;
964
+ gsum += amt; gcount++;
965
+ h.push('<tr><td class="c">' + n + '</td><td>' + esc(b.name) + '</td><td class="c">' + esc(b.unit) + '</td>'
966
+ + '<td class="r">' + (+b.price).toFixed(2) + '</td><td class="c">' + b.qty + '</td>'
967
+ + '<td class="r">' + amt.toFixed(2) + '</td></tr>');
968
+ }
969
+ flushGroup();
970
+ h.push('<tr class="sub2"><td colspan="5" class="r">设备总价</td><td class="r">' + sub.toFixed(2) + '</td></tr>');
971
+ h.push('<tr class="sub2"><td colspan="5" class="r">设计·跟进·安装调试费(' + state.fee + '%)</td><td class="r">' + feeAmt.toFixed(2) + '</td></tr>');
972
+ h.push('<tr class="tot"><td colspan="5" class="r">合计(含安装调试)</td><td class="r">' + (sub + feeAmt).toFixed(2) + '</td></tr>');
973
+ h.push('</table>');
974
+ h.push('<div class="note">备注:<br>1. 以上报价含设备、设计、跟进与安装调试,最终以现场实测为准。<br>2. 本报价有效期 30 天,签字确认后生效。<br>3. 报价单号:' + stamp() + '-' + (state.bill.length) + '</div>');
975
+ h.push('</body></html>');
976
+ var html = h.join('\r\n');
977
+ var name = safeName(title) + '_' + stamp() + '.doc';
978
+ var blob = new Blob(['\ufeff' + html], { type:'application/msword;charset=utf-8' });
979
+ if(isWX){
980
+ copyText(toPlainText());
981
+ alert('微信内不能直接下载文件。\n报价单文本已复制,粘贴到 Word / WPS 里即可;\n或点右上角「…」选择「在浏览器打开」后重新导出。');
982
+ return;
983
+ }
984
+ if(dl(blob, name)){ toast('已导出:' + name); }
985
+ else{ toast('导出被浏览器拦截,请改用「打印 / 另存为 PDF」'); }
986
+ }
987
+ function toPlainText(){
988
+ var h = [], sub = 0;
989
+ h.push('智能家居配置报价单');
990
+ h.push(quoteTitle());
991
+ state.bill.forEach(function(b, i){
992
+ var amt = b.qty * b.price; sub += amt;
993
+ h.push((i+1) + '. ' + b.name + ' ' + b.price + '元/' + b.unit + ' × ' + b.qty + ' = ' + (Math.round(amt*100)/100) + '元');
994
+ });
995
+ var feeAmt = sub * state.fee / 100;
996
+ h.push('设备总价:' + Math.round(sub*100)/100 + ' 元');
997
+ h.push('设计·跟进·安装调试费(' + state.fee + '%):' + Math.round(feeAmt*100)/100 + ' 元');
998
+ h.push('合计:' + Math.round((sub+feeAmt)*100)/100 + ' 元');
999
+ return h.join('\r\n');
1000
+ }
1001
+ function exportCSV(){
1002
+ if(!state.bill.length){ toast('明细为空,先加入产品'); return; }
1003
+ var lines = ['板块,子类,产品名称,单位,单价,数量,小计'], sub = 0;
1004
+ state.bill.forEach(function(b){
1005
+ var s = b.qty * b.price; sub += s;
1006
+ lines.push([b.cat, b.sub, b.name, b.unit, b.price, b.qty, Math.round(s*100)/100].map(function(v){
1007
+ v = String(v == null ? '' : v);
1008
+ return /[",\r\n]/.test(v) ? '"' + v.replace(/"/g, '""') + '"' : v;
1009
+ }).join(','));
1010
+ });
1011
+ var feeAmt = sub * state.fee / 100;
1012
+ lines.push('设备总价,,,,,' + Math.round(sub*100)/100);
1013
+ lines.push('服务费(' + state.fee + '%),,,,,' + Math.round(feeAmt*100)/100);
1014
+ lines.push('合计,,,,,' + Math.round((sub+feeAmt)*100)/100);
1015
+ var blob = new Blob(['\ufeff' + lines.join('\r\n')], { type:'text/csv;charset=utf-8' });
1016
+ var name = safeName(quoteTitle()) + '_' + stamp() + '.csv';
1017
+ if(isWX){ copyText(lines.join('\n')); alert('微信内不能下载,CSV 内容已复制到剪贴板。'); return; }
1018
+ if(dl(blob, name)){ toast('已导出:' + name); }
1019
+ else{ toast('导出被拦截,请用「打印 / 另存为 PDF」'); }
1020
+ }
1021
+ function copyText(txt){
1022
+ try{
1023
+ var ta = document.createElement('textarea');
1024
+ ta.value = txt;
1025
+ ta.style.cssText = 'position:fixed;left:-9999px;top:0';
1026
+ document.body.appendChild(ta);
1027
+ ta.select(); ta.setSelectionRange(0, ta.value.length);
1028
+ document.execCommand('copy');
1029
+ document.body.removeChild(ta);
1030
+ return true;
1031
+ }catch(e){ return false; }
1032
+ }
1033
+
1034
+ /* ---------- 模态框 ---------- */
1035
+ function show(id){ $(id).classList.add('on'); }
1036
+ function closeModal(id){ $(id).classList.remove('on'); }
1037
+ function closeAll(){ document.querySelectorAll('.modal').forEach(function(m){ m.classList.remove('on'); }); }
1038
+
1039
+ /* ---------- 事件 ---------- */
1040
+ $('nav').addEventListener('click', function(e){
1041
+ var el = e.target.closest('.navi'); if(!el) return;
1042
+ state.ci = +el.dataset.ci; state.si = -1; state.kw = '';
1043
+ $('kw').value = ''; $('kwClr').classList.remove('on');
1044
+ renderNav(); renderStage();
1045
+ });
1046
+ $('chips').addEventListener('click', function(e){
1047
+ var el = e.target.closest('.chip'); if(!el) return;
1048
+ state.si = +el.dataset.si; renderStage();
1049
+ });
1050
+ $('plist').addEventListener('click', function(e){
1051
+ var del = e.target.closest('[data-pdel]');
1052
+ if(del){
1053
+ e.stopPropagation();
1054
+ var id = del.dataset.pdel, it = null;
1055
+ DB.items.forEach(function(x){ if(x.id === id) it = x; });
1056
+ if(!it) return;
1057
+ if(!confirm('从产品库删除产品「' + it.name + '」?\n删除后不再出现在产品列表(已加入的明细不受影响)。')) return;
1058
+ DB.items = DB.items.filter(function(x){ return x.id !== id; });
1059
+ saveDB(); renderAll(); renderList();
1060
+ if($('mMgr').classList.contains('on')) renderMgr();
1061
+ toast('已删除:' + it.name);
1062
+ return;
1063
+ }
1064
+ var card = e.target.closest('.pcard'); if(!card) return;
1065
+ var pid = card.dataset.id, target = null;
1066
+ DB.items.forEach(function(x){ if(x.id === pid) target = x; });
1067
+ if(target){ addProduct(target); toast('已加入:' + target.name); }
1068
+ });
1069
+ function onKW(){
1070
+ state.kw = $('kw').value;
1071
+ $('kwClr').classList.toggle('on', !!state.kw);
1072
+ renderList();
1073
+ }
1074
+ var kwTimer = null;
1075
+ $('kw').addEventListener('input', function(){
1076
+ clearTimeout(kwTimer); kwTimer = setTimeout(onKW, 110);
1077
+ });
1078
+ $('kwClr').addEventListener('click', function(){
1079
+ $('kw').value = ''; state.kw = ''; this.classList.remove('on'); renderList();
1080
+ });
1081
+ $('billbody').addEventListener('click', function(e){
1082
+ var d = e.target.closest('[data-del]');
1083
+ if(d){ state.bill.splice(+d.dataset.del, 1); renderBill(); renderList(); return; }
1084
+ });
1085
+ $('billbody').addEventListener('input', function(e){
1086
+ var t = e.target;
1087
+ if(t.dataset.qty !== undefined){ state.bill[+t.dataset.qty].qty = Math.max(0, +t.value || 0); updateSums(); saveSess(); }
1088
+ else if(t.dataset.price !== undefined){ state.bill[+t.dataset.price].price = Math.max(0, +t.value || 0); updateSums(); saveSess(); }
1089
+ });
1090
+ $('fee').addEventListener('input', function(){
1091
+ state.fee = Math.max(0, Math.min(100, +this.value || 0));
1092
+ updateSums(); saveSess();
1093
+ });
1094
+ $('btnSaveQuote').addEventListener('click', function(){ saveQuote(false); });
1095
+ $('ebSave').addEventListener('click', function(){ saveQuote(false); });
1096
+ $('ebNew').addEventListener('click', function(){ saveQuote(true); });
1097
+ $('ebExit').addEventListener('click', function(){
1098
+ state.editing = null; renderEditBar(); saveSess(); toast('已退出编辑,当前明细保留');
1099
+ });
1100
+ $('btnHist').addEventListener('click', function(){ renderHist(); show('mHist'); });
1101
+ $('btnMgr').addEventListener('click', function(){ openMgr(); });
1102
+ $('btnWord').addEventListener('click', exportWord);
1103
+ $('btnMore').addEventListener('click', function(){ show('mMore'); });
1104
+ $('btnClear').addEventListener('click', function(){
1105
+ if(!state.bill.length){ toast('明细已经是空的'); return; }
1106
+ if(!confirm('清空全部 ' + state.bill.length + ' 条明细?(历史报价不受影响)')) return;
1107
+ state.bill = []; state.editing = null; renderBill(); renderList(); renderEditBar(); saveSess(); toast('已清空');
1108
+ });
1109
+ document.querySelectorAll('[data-close]').forEach(function(b){
1110
+ b.addEventListener('click', function(){ closeModal(b.dataset.close); });
1111
+ });
1112
+ document.querySelectorAll('.modal').forEach(function(m){
1113
+ m.addEventListener('click', function(e){ if(e.target === m) m.classList.remove('on'); });
1114
+ });
1115
+ $('mWord').addEventListener('click', function(){ closeModal('mMore'); exportWord(); });
1116
+ $('mCsv').addEventListener('click', function(){ closeModal('mMore'); exportCSV(); });
1117
+ $('mCopy').addEventListener('click', function(){
1118
+ if(!state.bill.length){ toast('明细为空'); return; }
1119
+ copyText(toPlainText());
1120
+ closeModal('mMore'); toast('明细已复制,可粘贴到微信/Word');
1121
+ });
1122
+ $('mPrint').addEventListener('click', function(){ closeModal('mMore'); setTimeout(function(){ window.print(); }, 120); });
1123
+
1124
+ /* 历史报价事件 */
1125
+ $('histBody').addEventListener('click', function(e){
1126
+ var o = e.target.closest('[data-open]');
1127
+ if(o){ openQuote(o.dataset.open); return; }
1128
+ var r = e.target.closest('[data-rename]');
1129
+ if(r){
1130
+ var list = histAll(), rec = null;
1131
+ list.forEach(function(q){ if(q.id === r.dataset.rename) rec = q; });
1132
+ if(!rec) return;
1133
+ var v = prompt('新的报价单名称', rec.name);
1134
+ if(v === null) return;
1135
+ v = String(v).trim(); if(!v) return;
1136
+ rec.name = v; histPut(list);
1137
+ if(state.editing && state.editing.id === rec.id){ state.editing.name = v; renderEditBar(); saveSess(); }
1138
+ renderHist(); toast('已改名');
1139
+ return;
1140
+ }
1141
+ var d = e.target.closest('[data-delq]');
1142
+ if(d){
1143
+ var arr = histAll(), t = null;
1144
+ arr.forEach(function(q){ if(q.id === d.dataset.delq) t = q; });
1145
+ if(!t) return;
1146
+ if(!confirm('删除历史报价「' + t.name + '」?不可恢复。')) return;
1147
+ histPut(arr.filter(function(q){ return q.id !== t.id; }));
1148
+ if(state.editing && state.editing.id === t.id){ /* 仍保留明细编辑状态 */ }
1149
+ renderHist(); toast('已删除');
1150
+ }
1151
+ });
1152
+
1153
+ /* 产品库管理事件 */
1154
+ $('catList').addEventListener('click', function(e){
1155
+ var ren = e.target.closest('[data-rencat]');
1156
+ if(ren){
1157
+ var i = +ren.dataset.rencat, v = prompt('类目名称', DB.cats[i].n);
1158
+ if(v === null) return;
1159
+ v = String(v).trim(); if(!v) return;
1160
+ DB.cats[i].n = v; saveDB(); renderMgr(); renderAll(); toast('已改名');
1161
+ return;
1162
+ }
1163
+ var del = e.target.closest('[data-delcat]');
1164
+ if(del){ delCat(+del.dataset.delcat); return; }
1165
+ var li = e.target.closest('.mli');
1166
+ if(li){ mgr.ci = +li.dataset.ci; mgr.si = 0; renderMgr(); }
1167
+ });
1168
+ $('subList').addEventListener('click', function(e){
1169
+ var ren = e.target.closest('[data-rensub]');
1170
+ if(ren){
1171
+ var j = +ren.dataset.rensub, cat = DB.cats[mgr.ci];
1172
+ var v = prompt('子类名称', cat.subs[j]);
1173
+ if(v === null) return;
1174
+ v = String(v).trim(); if(!v) return;
1175
+ cat.subs[j] = v; saveDB(); renderMgr(); renderAll(); toast('已改名');
1176
+ return;
1177
+ }
1178
+ var del = e.target.closest('[data-delsub]');
1179
+ if(del){ delSub(mgr.ci, +del.dataset.delsub); return; }
1180
+ var li = e.target.closest('.mli');
1181
+ if(li){ mgr.si = +li.dataset.si; renderItemRows(); renderSubList(); }
1182
+ });
1183
+ $('mgrKw').addEventListener('input', function(){ mgr.kw = this.value; renderItemRows(); });
1184
+ $('itemRows').addEventListener('change', function(e){
1185
+ var t = e.target, f = t.dataset.f;
1186
+ if(!f) return;
1187
+ var row = t.closest('.mtr'), id = row.dataset.id, it = null;
1188
+ DB.items.forEach(function(x){ if(x.id === id) it = x; });
1189
+ if(!it) return;
1190
+ if(f === 'price'){ it.price = Math.max(0, +t.value || 0); t.value = it.price; }
1191
+ else{
1192
+ var v = String(t.value).trim();
1193
+ if(!v){ t.value = it[f]; return; }
1194
+ it[f] = v;
1195
+ }
1196
+ saveDB(); renderNav(); renderStage(); renderMgr();
1197
+ });
1198
+ $('itemRows').addEventListener('click', function(e){
1199
+ var d = e.target.closest('[data-delitem]');
1200
+ if(!d) return;
1201
+ var id = d.dataset.delitem, it = null;
1202
+ DB.items.forEach(function(x){ if(x.id === id) it = x; });
1203
+ if(!it) return;
1204
+ if(!confirm('删除产品「' + it.name + '」?')) return;
1205
+ DB.items = DB.items.filter(function(x){ return x.id !== id; });
1206
+ saveDB(); renderMgr(); renderAll(); toast('已删除');
1207
+ });
1208
+ $('btnAddCat').addEventListener('click', function(){
1209
+ var v = prompt('新类目名称(如:智能照明)', '');
1210
+ if(v === null) return;
1211
+ v = String(v).trim(); if(!v) return;
1212
+ DB.cats.push({ n: v, subs: ['默认子类'] });
1213
+ mgr.ci = DB.cats.length - 1; mgr.si = 0;
1214
+ saveDB(); renderMgr(); renderAll(); toast('已新增类目');
1215
+ });
1216
+ $('btnAddSub').addEventListener('click', function(){
1217
+ var cat = DB.cats[mgr.ci];
1218
+ if(!cat){ toast('先建一个类目'); return; }
1219
+ var v = prompt('新子类名称', '');
1220
+ if(v === null) return;
1221
+ v = String(v).trim(); if(!v) return;
1222
+ cat.subs.push(v);
1223
+ mgr.si = cat.subs.length - 1;
1224
+ saveDB(); renderMgr(); renderAll(); toast('已新增子类');
1225
+ });
1226
+ $('btnAddItem').addEventListener('click', function(){
1227
+ var cat = DB.cats[mgr.ci];
1228
+ if(!cat){ toast('先建一个类目'); return; }
1229
+ if(cat.subs.length === 0){ cat.subs.push('默认子类'); mgr.si = 0; }
1230
+ if(mgr.si < 0) mgr.si = 0;
1231
+ $('addRow').style.display = 'flex';
1232
+ $('anName').value = ''; $('anUnit').value = '个'; $('anPrice').value = '';
1233
+ $('anName').focus();
1234
+ });
1235
+ $('anCancel').addEventListener('click', function(){ $('addRow').style.display = 'none'; });
1236
+ $('anSave').addEventListener('click', function(){
1237
+ var nm = String($('anName').value).trim(), un = String($('anUnit').value).trim() || '个', pr = Math.max(0, +$('anPrice').value || 0);
1238
+ if(!nm){ toast('产品名称不能为空'); return; }
1239
+ DB.seq = (DB.seq || 0) + 1;
1240
+ DB.items.push({ id:'u' + DB.seq + '_' + Date.now().toString(36), ci: mgr.ci, si: mgr.si, name: nm, unit: un, price: pr, alias: [] });
1241
+ saveDB(); $('addRow').style.display = 'none'; renderMgr(); renderAll(); toast('已新增产品:' + nm);
1242
+ });
1243
+ /* ---------- 产品库备份 / 恢复 ---------- */
1244
+ function stamp(){
1245
+ var d = new Date(), p = function(n){ return (n < 10 ? '0' : '') + n; };
1246
+ return d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + '_' + p(d.getHours()) + p(d.getMinutes());
1247
+ }
1248
+ $('btnExpDB').addEventListener('click', function(){
1249
+ var payload = { type:'slzj-db', ver:1, rev:REV, exportedAt:new Date().toISOString(), db:DB };
1250
+ var blob = new Blob([JSON.stringify(payload, null, 1)], { type:'application/json;charset=utf-8' });
1251
+ dl(blob, '树懒产品库备份_' + stamp() + '.json');
1252
+ toast('已导出备份:' + DB.cats.length + ' 类 / ' + DB.items.length + ' 款产品');
1253
+ });
1254
+ $('btnImpDB').addEventListener('click', function(){ $('impFile').click(); });
1255
+ $('impFile').addEventListener('change', function(e){
1256
+ var f = e.target.files && e.target.files[0];
1257
+ if(!f) return;
1258
+ var fr = new FileReader();
1259
+ fr.onload = function(){
1260
+ try{
1261
+ var j = JSON.parse(String(fr.result));
1262
+ var db = (j && j.db) ? j.db : j;
1263
+ if(!db || !db.cats || !db.cats.length || !db.items || !db.items.length) throw new Error('不是有效的产品库备份');
1264
+ var ni = 0;
1265
+ for(var i = 0; i < db.items.length; i++){
1266
+ var it = db.items[i];
1267
+ if(!it || !it.name) continue;
1268
+ if(!it.id) it.id = 'u' + i + '_' + Date.now().toString(36);
1269
+ if(!it.unit) it.unit = '个';
1270
+ it.price = +it.price || 0;
1271
+ if(!it.alias) it.alias = [];
1272
+ ni++;
1273
+ }
1274
+ DB = { rev:REV, cats:db.cats.map(function(c){ return { n:c.n, subs:(c.subs || []).slice() }; }), items:db.items, seq:db.seq || 0 };
1275
+ saveDB();
1276
+ if(state.ci >= DB.cats.length) state.ci = 0;
1277
+ state.si = -1;
1278
+ renderAll(); renderMgr();
1279
+ toast('导入成功:' + DB.cats.length + ' 类 / ' + ni + ' 款产品');
1280
+ }catch(err){
1281
+ toast('导入失败:' + (err && err.message ? err.message : '文件格式不对'));
1282
+ }
1283
+ };
1284
+ fr.readAsText(f, 'utf-8');
1285
+ e.target.value = '';
1286
+ });
1287
+
1288
+ $('btnResetDB').addEventListener('click', function(){
1289
+ if(!confirm('恢复初始产品库?\n你新增/删除/修改的产品和类目都会丢失,历史报价不受影响。')) return;
1290
+ try{ localStorage.removeItem(K_DB); }catch(e){}
1291
+ location.reload();
1292
+ });
1293
+
1294
+ /* ================= 云同步:一份产品库,多人共用 ================= */
1295
+ function b64e(s){ try{ return btoa(unescape(encodeURIComponent(s))).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,''); }catch(e){ return ''; } }
1296
+ function b64d(s){ try{ return decodeURIComponent(escape(atob(String(s).replace(/-/g,'+').replace(/_/g,'/')))); }catch(e){ return ''; } }
1297
+
1298
+ var CFG = (function(){
1299
+ var c = null;
1300
+ try{ var r = localStorage.getItem(K_CLOUD); if(r) c = JSON.parse(r); }catch(e){}
1301
+ /* 扫码链接里带着配置(?c=...)时优先用链接的,客户无需任何设置 */
1302
+ try{
1303
+ var m = location.search.match(/[?&]c=([^&]+)/);
1304
+ if(m){
1305
+ var q = JSON.parse(b64d(m[1]));
1306
+ if(q && q.url && q.key && q.room){
1307
+ c = { url: q.url, key: q.key, room: q.room };
1308
+ try{ localStorage.setItem(K_CLOUD, JSON.stringify(c)); }catch(e){}
1309
+ }
1310
+ }
1311
+ }catch(e){}
1312
+ if(!c && CLOUD_DEFAULT.url && CLOUD_DEFAULT.key && CLOUD_DEFAULT.room) c = CLOUD_DEFAULT;
1313
+ return c;
1314
+ })();
1315
+ var cloudSkip = false, cloudBusy = false, cloudTimer = null, cloudWarned = false;
1316
+
1317
+ function cloudOn(){ return !!(CFG && CFG.url && CFG.key && CFG.room); }
1318
+ function cloudTS(){ try{ return +(localStorage.getItem(K_DBTS) || 0); }catch(e){ return 0; } }
1319
+
1320
+ function cloudBadge(cls, txt){
1321
+ var b = $('btnSync');
1322
+ if(b){ b.className = 'btn ghost sb-' + cls; b.innerHTML = '<i class="sdot"></i>' + txt; }
1323
+ var st = $('clState');
1324
+ if(st){ st.className = 'cst ' + cls; st.textContent = txt; }
1325
+ }
1326
+ function cloudBase(){
1327
+ var u = String(CFG.url || '').replace(/\s+/g, '').replace(/\/+$/, '');
1328
+ if(!/\/rest\/v1$/.test(u)) u += '/rest/v1';
1329
+ return u + '/slzj_db';
1330
+ }
1331
+ function cloudHdr(extra){
1332
+ var h = { 'apikey': CFG.key, 'Authorization': 'Bearer ' + CFG.key, 'Content-Type': 'application/json' };
1333
+ if(extra){ for(var k in extra) h[k] = extra[k]; }
1334
+ return h;
1335
+ }
1336
+
1337
+ /* 把本机产品库上传(用户增删改后自动触发) */
1338
+ function cloudPush(){
1339
+ if(!cloudOn() || cloudSkip || cloudBusy) return;
1340
+ cloudBusy = true; cloudBadge('ing', '同步中');
1341
+ var ts = new Date().toISOString();
1342
+ fetch(cloudBase() + '?on_conflict=room', {
1343
+ method: 'POST',
1344
+ cache: 'no-store',
1345
+ headers: cloudHdr({ 'Prefer': 'resolution=merge-duplicates,return=minimal' }),
1346
+ body: JSON.stringify([{ room: CFG.room, payload: DB, updated_at: ts }])
1347
+ }).then(function(r){
1348
+ if(!r.ok) return r.text().then(function(t){ throw new Error('HTTP ' + r.status + (t ? ' ' + t.slice(0, 90) : '')); });
1349
+ cloudBusy = false;
1350
+ try{ localStorage.setItem(K_DBTS, String(Date.parse(ts))); }catch(e){}
1351
+ cloudWarned = false;
1352
+ cloudBadge('ok', '已同步');
1353
+ }).catch(function(e){
1354
+ cloudBusy = false;
1355
+ cloudBadge('err', '未同步');
1356
+ if(!cloudWarned){ cloudWarned = true; toast('云同步失败:' + e.message); }
1357
+ });
1358
+ }
1359
+
1360
+ /* 用云端数据覆盖本机 */
1361
+ function cloudApply(p){
1362
+ if(!p || !p.cats || !p.cats.length || !p.items || !p.items.length) return false;
1363
+ cloudSkip = true;
1364
+ DB = { rev: REV, cats: p.cats, items: p.items, seq: p.seq || 0 };
1365
+ saveDB();
1366
+ cloudSkip = false;
1367
+ if(state.ci >= DB.cats.length) state.ci = 0;
1368
+ state.si = -1;
1369
+ try{ renderAll(); }catch(e){}
1370
+ try{ renderMgr(); }catch(e){}
1371
+ return true;
1372
+ }
1373
+
1374
+ /* 从云端拉取,云端更新则覆盖本机,本机更新则上传 */
1375
+ function cloudPull(manual){
1376
+ if(!cloudOn()){ if(manual) toast('请先填写并保存云端地址与密钥'); return; }
1377
+ if(cloudBusy){ if(manual) toast('正在同步,请稍候'); return; }
1378
+ cloudBusy = true;
1379
+ if(manual) cloudBadge('ing', '拉取中');
1380
+ var url = cloudBase() + '?select=payload,updated_at&room=eq.' + encodeURIComponent(CFG.room);
1381
+ fetch(url, { headers: cloudHdr(), cache: 'no-store' }).then(function(r){
1382
+ if(!r.ok) throw new Error('HTTP ' + r.status);
1383
+ return r.json();
1384
+ }).then(function(rows){
1385
+ cloudBusy = false;
1386
+ if(!rows || !rows.length){ cloudPush(); return; }
1387
+ var row = rows[0], rp = row.payload, rts = Date.parse(row.updated_at) || 0;
1388
+ var same = JSON.stringify(rp && rp.items) === JSON.stringify(DB.items) &&
1389
+ JSON.stringify(rp && rp.cats) === JSON.stringify(DB.cats);
1390
+ if(same){
1391
+ try{ localStorage.setItem(K_DBTS, String(Math.max(rts, cloudTS()))); }catch(e){}
1392
+ cloudBadge('ok', '已同步');
1393
+ if(manual) toast('已是最新版本');
1394
+ return;
1395
+ }
1396
+ if(rts > cloudTS() + 1000){
1397
+ if(cloudApply(rp)){
1398
+ try{ localStorage.setItem(K_DBTS, String(rts)); }catch(e){}
1399
+ cloudBadge('ok', '已更新');
1400
+ toast('产品库已同步到最新(' + DB.items.length + ' 款)');
1401
+ }else{
1402
+ cloudBadge('err', '云端数据异常');
1403
+ }
1404
+ }else{
1405
+ cloudPush();
1406
+ }
1407
+ }).catch(function(e){
1408
+ cloudBusy = false;
1409
+ cloudBadge('err', '未连接');
1410
+ if(manual) toast('拉取失败:' + e.message);
1411
+ });
1412
+ }
1413
+
1414
+ function cloudStart(){
1415
+ if(cloudTimer){ clearInterval(cloudTimer); cloudTimer = null; }
1416
+ if(!cloudOn()){ cloudBadge('off', '未开启'); return; }
1417
+ cloudBadge('ing', '连接中');
1418
+ cloudPull(false);
1419
+ cloudTimer = setInterval(function(){ cloudPull(false); }, 20000);
1420
+ }
1421
+
1422
+ function cloudFillForm(){
1423
+ $('clUrl').value = CFG ? (CFG.url || '') : '';
1424
+ $('clKey').value = CFG ? (CFG.key || '') : '';
1425
+ $('clRoom').value = CFG ? (CFG.room || '') : 'slzj';
1426
+ }
1427
+
1428
+ $('btnSync').addEventListener('click', function(){ cloudFillForm(); show('mCloud'); });
1429
+
1430
+ $('clSave').addEventListener('click', function(){
1431
+ var u = $('clUrl').value.replace(/\s+/g, ''), k = $('clKey').value.replace(/\s+/g, ''), rm = $('clRoom').value.replace(/\s+/g, '');
1432
+ if(!/^https?:\/\//.test(u)){ toast('云端地址要以 https:// 开头'); return; }
1433
+ if(k.length < 20){ toast('接口密钥看起来不完整'); return; }
1434
+ if(!rm){ toast('请填写同步口令'); return; }
1435
+ CFG = { url: u, key: k, room: rm };
1436
+ try{ localStorage.setItem(K_CLOUD, JSON.stringify(CFG)); }catch(e){}
1437
+ closeModal('mCloud');
1438
+ cloudWarned = false;
1439
+ cloudStart();
1440
+ toast('云同步已开启:' + rm);
1441
+ });
1442
+
1443
+ $('clPush').addEventListener('click', function(){
1444
+ if(!cloudOn()){ toast('请先保存云端地址与密钥'); return; }
1445
+ cloudWarned = false;
1446
+ cloudPush();
1447
+ });
1448
+
1449
+ $('clPull').addEventListener('click', function(){ cloudPull(true); });
1450
+
1451
+ $('clShare').addEventListener('click', function(){
1452
+ if(!cloudOn()){ toast('请先保存云端地址与密钥'); return; }
1453
+ var base = location.href.split('#')[0].split('?')[0];
1454
+ var link = base + '?c=' + b64e(JSON.stringify(CFG));
1455
+ if(/^file:/.test(base)){
1456
+ $('clTip').innerHTML = '当前是本地文件方式打开,这个链接只能本机用。把页面放到网址上(如局域网服务或公网托管)后再来生成,客户扫码即可自动同步。';
1457
+ return;
1458
+ }
1459
+ $('clTip').innerHTML = '分享链接已复制,做成二维码给客户扫:<br><b style="word-break:break-all;color:#1c4e80">' + esc(link) + '</b>';
1460
+ copyText(link);
1461
+ toast('分享链接已复制');
1462
+ });
1463
+
1464
+ $('clOff').addEventListener('click', function(){
1465
+ if(!cloudOn()){ toast('当前未开启云同步'); return; }
1466
+ if(!confirm('关闭云同步?\n关闭后本机产品库照常可用,但不会再与其他设备同步。')) return;
1467
+ CFG = null;
1468
+ try{ localStorage.removeItem(K_CLOUD); }catch(e){}
1469
+ if(cloudTimer){ clearInterval(cloudTimer); cloudTimer = null; }
1470
+ cloudBadge('off', '未开启');
1471
+ closeModal('mCloud');
1472
+ toast('已关闭云同步');
1473
+ });
1474
+
1475
+ document.addEventListener('visibilitychange', function(){
1476
+ if(!document.hidden && cloudOn()) cloudPull(false);
1477
+ });
1478
+
1479
+ /* ---------- 启动 ---------- */
1480
+ loadSess();
1481
+ if(state.ci >= DB.cats.length) state.ci = 0;
1482
+ $('fee').value = state.fee;
1483
+ renderAll();
1484
+ cloudStart();
1485
+
1486
+ /* ---------- PWA:注册 Service Worker(仅 http/https 生效) ---------- */
1487
+ (function(){
1488
+ if(!('serviceWorker' in navigator)) return;
1489
+ if(!/^https?:$/.test(location.protocol)) return;
1490
+ window.addEventListener('load', function(){
1491
+ /* sw.js 与页面同目录;unpkg 会 302 到 @版本目录,用相对路径可保证同源同目录命中 */
1492
+ navigator.serviceWorker.register('sw.js').catch(function(){});
1493
+ });
1494
+ })();
1495
+ </script>
1496
+ </body>
1497
+ </html>