efront 3.12.6 → 3.13.4

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.
Files changed (61) hide show
  1. package/apps/pivot/api.yml +8 -0
  2. package/apps/pivot/home/welcome.html +1 -1
  3. package/apps/pivot/home/welcome.js +6 -9
  4. package/apps/pivot/log/boot.html +2 -0
  5. package/apps/pivot/log/boot.js +39 -0
  6. package/apps/pivot/log/boot.less +11 -0
  7. package/apps/pivot/log/count.html +5 -0
  8. package/apps/pivot/log/count.js +22 -0
  9. package/apps/pivot/log/count.less +16 -0
  10. package/apps/pivot/main.js +9 -10
  11. package/apps/pivot/menu.yml +8 -2
  12. package/apps/pivot/proxy/edit.js +1 -0
  13. package/apps/pivot/proxy/list.js +12 -0
  14. package/apps/pivot/share/list.less +0 -4
  15. package/apps/pivot/user/edit.js +1 -0
  16. package/apps/pivot/user/list.js +4 -0
  17. package/apps/pivot/user/tag/edit.js +1 -0
  18. package/apps/pivot/user/tag/list.js +3 -0
  19. package/coms/basic/cross_.js +8 -1
  20. package/coms/basic/encodePack.js +2 -9
  21. package/coms/basic/parseURL_test.js +2 -0
  22. package/coms/basic/parseYML.js +1 -1
  23. package/coms/basic/renderExpress.js +1 -1
  24. package/coms/frame/route.js +4 -0
  25. package/coms/pivot/plist.js +1 -1
  26. package/coms/zimoli/AudioContext_test.html +1 -1
  27. package/coms/zimoli/AudioContext_test.js +3 -3
  28. package/coms/zimoli/bind.js +4 -2
  29. package/coms/zimoli/cloneVisible.js +9 -2
  30. package/coms/zimoli/cross.js +2 -3
  31. package/coms/zimoli/data.js +22 -4
  32. package/coms/zimoli/drag.js +3 -2
  33. package/coms/zimoli/field.html +15 -10
  34. package/coms/zimoli/menu.js +33 -13
  35. package/coms/zimoli/menu.less +31 -9
  36. package/coms/zimoli/menuItem.js +1 -1
  37. package/coms/zimoli/menuList.html +5 -3
  38. package/coms/zimoli/menuList.js +63 -28
  39. package/coms/zimoli/menuList.less +5 -0
  40. package/coms/zimoli/model.js +22 -1
  41. package/coms/zimoli/on.js +5 -3
  42. package/coms/zimoli/picture.js +30 -335
  43. package/coms/zimoli/picture_.js +356 -0
  44. package/coms/zimoli/prompt.js +3 -1
  45. package/coms/zimoli/render.js +22 -10
  46. package/coms/zimoli/renderDefaults.js +1 -0
  47. package/coms/zimoli/search.js +5 -4
  48. package/coms/zimoli/select.js +7 -3
  49. package/coms/zimoli/selectList.js +7 -7
  50. package/coms/zimoli/selectListEdit.js +1 -1
  51. package/coms/zimoli/success.js +4 -0
  52. package/coms/zimoli/success.less +13 -0
  53. package/coms/zimoli/table.html +6 -8
  54. package/coms/zimoli/table.js +25 -2
  55. package/coms/zimoli/table.less +24 -4
  56. package/coms/zimoli/view.less +4 -0
  57. package/package.json +1 -1
  58. package/public/efront.js +1 -1
  59. package/apps/pivot/home/short.html +0 -1
  60. package/apps/pivot/home/short.js +0 -5
  61. package/apps/pivot/home/short.less +0 -1
@@ -10,3 +10,11 @@
10
10
  invoke: options ::invoke-:key?:params
11
11
  version: options ::version
12
12
  uptime: options ::uptime
13
+ count: options ::count
14
+ bootlog: options ::similar
15
+ https://www.ip.cn/ :
16
+ iplocation: get https://www.ip.cn/api/index?ip=:ip&type=1
17
+ # https://ipchaxun.com/ :
18
+ # iplocation: get:[].info%20label:nth-child(n+2)>span:nth-child(2) :ip/
19
+ # https://www.ip138.com/:
20
+ # iplocation: get:head>script:nth-last-child(1) iplookup.asp?ip=:ip&action=2
@@ -25,4 +25,4 @@
25
25
  <padding>
26
26
  启动时间&nbsp;&nbsp;<span -text="filterTime(hrtime)"></span>
27
27
  </padding>
28
- </block>
28
+ </block>
@@ -6,15 +6,12 @@ function main() {
6
6
  version: data.from("version"),
7
7
  hrtime: data.from("uptime", a => Date.now() - a * 1000),
8
8
  filterTime,
9
- async run(id, target) {
10
- target.setAttribute('pending', '')
11
- try {
12
- var info = await data.from("run", {
13
- run: id
14
- }).loading_promise;
15
- if (info) alert(info, 'pass');
16
- } catch { }
17
- target.removeAttribute('pending');
9
+ async run(id) {
10
+ await new Promise(ok => setTimeout(ok, 2000));
11
+ var info = await data.from("run", {
12
+ run: id
13
+ }).loading_promise;
14
+ if (info) alert(info, 'pass');
18
15
  },
19
16
  async logout() {
20
17
  data.setSource({});
@@ -0,0 +1,2 @@
1
+ <table -src="[fields,items]">
2
+ </talbe>
@@ -0,0 +1,39 @@
1
+ var fields = refilm`
2
+ 地址/ip
3
+ 地理位置/ip ${function (e) {
4
+ var ip = e.data[e.field.key];
5
+ var m = /(\d+\.){3}\d+$/.exec(ip);
6
+ if (m) {
7
+ data.from("iplocation", { ip: m[0] }, function (a) {
8
+ var l = document.createElement('label');
9
+ l.innerText = a.address;
10
+ appendChild(e, l);
11
+ });
12
+ }
13
+ return e;
14
+ }}
15
+ 启动时间/time ${function (e) {
16
+ e.innerHTML = filterTime(e.data[e.field.key]);
17
+ }}
18
+ 端口/port input
19
+ 版本/version
20
+ 进程/pid
21
+ `;
22
+ function main() {
23
+ var page = div();
24
+ page.innerHTML = template;
25
+ var items = data.from("bootlog", datas => {
26
+ datas.forEach(a => {
27
+ var [v, p] = a.ppid.split("/");
28
+ a.version = v;
29
+ a.pid = p;
30
+ });
31
+ datas.sort((a, b) => b.time - a.time);
32
+ return datas;
33
+ });
34
+ renderWithDefaults(page, {
35
+ items,
36
+ fields
37
+ });
38
+ return page;
39
+ }
@@ -0,0 +1,11 @@
1
+ & {
2
+ height: 100%;
3
+ }
4
+
5
+ model {
6
+ label {
7
+ display: inline-block;
8
+ color: #666;
9
+ font-size: 9px;
10
+ }
11
+ }
@@ -0,0 +1,5 @@
1
+ <div class="searchbox">
2
+ <input @input="filter" -model="searchText" />
3
+ </div>
4
+ <table -src="[fields,items]">
5
+ </talbe>
@@ -0,0 +1,22 @@
1
+ var fields = refilm`
2
+ 路径/path html
3
+ 访问量/count
4
+ `;
5
+ function main() {
6
+ var page = div();
7
+ page.innerHTML = template;
8
+ var items = data.from("count", a => {
9
+ return Object.keys(a).map(b => ({ path: b, count: a[b] }));
10
+ });
11
+ renderWithDefaults(page, {
12
+ items,
13
+ searchText: "",
14
+ filter() {
15
+ var text = this.searchText;
16
+ if (!text) return this.items = items;
17
+ this.items = search(text, items, 'path');
18
+ },
19
+ fields
20
+ });
21
+ return page;
22
+ }
@@ -0,0 +1,16 @@
1
+ & {
2
+ height: 100%;
3
+
4
+ }
5
+
6
+ .searchbox {
7
+ position: absolute;
8
+ top: 0;
9
+ right: 0;
10
+ z-index: 2;
11
+ padding: 8px 16px;
12
+
13
+ >input {
14
+ background: #eee;
15
+ }
16
+ }
@@ -7,16 +7,15 @@ cross.addReform(async function ({ status, url, headers }, reform, reject) {
7
7
  var base1 = protocol + "//" + host + "/";
8
8
  if (base !== base1) {
9
9
  data.setSource(base1, null);
10
- var page = await popup("/auth/login", base1);
11
- care(page, "login", function (info) {
12
- data.setSource(base1, info);
13
- headers.authorization = info;
14
- reform();
15
- });
16
- on("remove")(page, reject);
17
- return false;
18
10
  }
19
- location.reload();
11
+ var page = await popup("/auth/login", base1);
12
+ care(page, "login", function (info) {
13
+ data.setSource(base1, info);
14
+ headers.authorization = info;
15
+ reform();
16
+ });
17
+ on("remove")(page, reject);
18
+ return false;
20
19
  }
21
20
  })
22
21
  data.setReporter(function (m, t) {
@@ -27,7 +26,7 @@ data.bindInstance("base", async function (base) {
27
26
  cross.addDirect(base.base);
28
27
  var apimap = await data.getConfig();
29
28
  for (var k in apimap) {
30
- apimap[k].base = base.base;
29
+ if (apimap[k].method === 'options') apimap[k].base = base.base;
31
30
  }
32
31
  });
33
32
  var base = data.getInstance('base').base;
@@ -1,11 +1,17 @@
1
1
  首页: /home/welcome
2
2
  WEB:
3
3
  共享目录: /share/list
4
- 短链接: /home/short
4
+ 短链接: /proxy/list
5
5
  文件管理: /wow/root
6
6
  长连接管理: /link/list
7
7
  任务:
8
8
  密钥管理: /token/list
9
9
  任务建立: /task/list
10
10
  定期执行: /tick/list
11
- # 用户列表: /user/list
11
+ 用户:
12
+ 账号列表: /user/list
13
+ 标签管理: /user/tag/list
14
+ 服务器日志:
15
+ 访问计数: /log/count
16
+ 启动记录: /log/boot
17
+ # 并发记忆: /log/lock
@@ -0,0 +1 @@
1
+ pedit.bind(null, "短链接", "proxy");
@@ -0,0 +1,12 @@
1
+ var fields = refilm`
2
+ *代理路径/url input/20
3
+ 真实路径/realpath url/2000
4
+ / - ${[{
5
+ name: "访问",
6
+ do(o) {
7
+ var url = o.url;
8
+ if (!/^\//.test(url)) url = "/" + url;
9
+ window.open(url,null);
10
+ }
11
+ }]}`;
12
+ return plist.bind(null, '短链接', "proxy", fields, "/proxy/edit");
@@ -1,7 +1,3 @@
1
- >table {
2
- width: 100%;
3
- }
4
-
5
1
  & {
6
2
  height: 100%;
7
3
  }
@@ -0,0 +1 @@
1
+ pedit.bind(null, "用户信息", "user");
@@ -0,0 +1,4 @@
1
+ plist.bind(null, '用户管理', "user", refilm`
2
+ *用户ID/key 100
3
+ 用户名/name input/300
4
+ `, "/user/edit");
@@ -0,0 +1 @@
1
+ pedit.bind(null, "标签", "tag");
@@ -0,0 +1,3 @@
1
+ plist.bind(null, '标签', "tag", refilm`
2
+ *标签/key 100
3
+ `, "/user/tag/edit");
@@ -180,7 +180,14 @@ function cross_(jsonp, digest = noop, method, url, headers) {
180
180
  };
181
181
  xhr.form = function (data) {
182
182
  xhr.data(data);
183
- if (FormData) {
183
+ var hasFile = false;
184
+ if (FormData) for (var k in jsondata) {
185
+ if (isFile(jsondata[k])) {
186
+ hasFile = true;
187
+ break;
188
+ }
189
+ }
190
+ if (hasFile) {
184
191
  datas = new FormData;
185
192
  for (var k in jsondata) {
186
193
  datas.append(k, jsondata[k]);
@@ -277,27 +277,20 @@ function pack2(buff) {
277
277
  var result = [];
278
278
  for (var cx = 0, dx = buff.length, bx = 32 * 1024 * 1024; cx < dx; cx += bx) {
279
279
  var b = buff.slice(cx, cx + bx);
280
- var [l, p, b] = scan(b);
281
- l = encodeRange(l);
282
- p = encodeRange(p);
280
+ var b = scan(b);
283
281
  var e = encodeRange(b);
284
282
  var el = int(e.length);
285
283
  result.push(concatTypedArray([
286
284
  [
287
285
  range_compress,
288
286
  other_compress << 5 | el.length,
289
- l.length >> 8,
290
- l.length & 0xff,
291
- p.length >> 8,
292
- p.length & 0xff
293
287
  ],
294
- el, l, p, e]));
288
+ el, e]));
295
289
  }
296
290
  result = concatTypedArray(result);
297
291
  return pack0(buff, result);
298
292
  }
299
293
  module.exports = function (buff, type) {
300
- type = 1;
301
294
  switch (type) {
302
295
  case 1:
303
296
  return pack(buff);
@@ -12,6 +12,8 @@ test('http://yunxu1019@live.cn@github.com/?a', 'username', 'yunxu1019@live.cn')
12
12
  test('localhost', 'host', 'localhost')
13
13
  test('localhost:80', 'port', '80')
14
14
  test('localhost:80/', 'port', '80')
15
+ test('efront.cc', 'host', 'efront.cc')
16
+ test('127.0.0.1', 'host', '127.0.0.1')
15
17
  test(':80', 'port', '80')
16
18
  test('80', 'port', '80')
17
19
  test('/80', 'pathname', '/80')
@@ -207,7 +207,7 @@ var scan = function (text) {
207
207
  }
208
208
  }
209
209
 
210
- if (row === "|") {
210
+ if (row === "|" || row === ">") {
211
211
  rowtype = "|"
212
212
  continue;
213
213
  }
@@ -3,7 +3,7 @@ function createSeek(express) {
3
3
  express.forEach(function (search) {
4
4
  if (dist) {
5
5
  if (/[\=]/.test(dist)) dist = `(${dist})`;
6
- dist = `${dist}!==void 0&&${dist}!==null?${dist}${search}:''`
6
+ dist = `typeof ${dist}!=='undefined'&&${dist}!==null?${dist}${search}:''`
7
7
  } else {
8
8
  dist = search;
9
9
  }
@@ -75,6 +75,7 @@
75
75
  if (/,/.test(name)) {
76
76
 
77
77
  }
78
+ item.closed = true;
78
79
  return item;
79
80
  });
80
81
  return items;
@@ -114,6 +115,7 @@
114
115
  actived = mmap[opened.active] || actived;
115
116
  if (actived) {
116
117
  if (actived_value === historys.length) {
118
+ setActive(actived, true);
117
119
  result.active = actived;
118
120
  };
119
121
  }
@@ -123,6 +125,7 @@
123
125
  var setActive = function (p, active) {
124
126
  while (p) {
125
127
  p.active = active;
128
+ if (active) p.closed = false;
126
129
  p = p.parent;
127
130
  }
128
131
  };
@@ -195,6 +198,7 @@
195
198
  result.then = then;
196
199
  return result;
197
200
  };
201
+ result.parse = parseMenuList;
198
202
  var then = function (ok, oh) {
199
203
  if (this.loading_promise) {
200
204
  return this.loading_promise.then(ok, oh);
@@ -2,7 +2,7 @@
2
2
  function main(title, type, fields, edit_ref, options) {
3
3
  return frame$list(title, {
4
4
  load() {
5
- return data.from("list", { type }, a => JSAM.parse(encode62.timedecode(a)));
5
+ return data.from("list", { type }, a => JSAM.parse(encode62.timedecode(a || '')));
6
6
  },
7
7
  remove(o) {
8
8
  return data.from("edit", { type, key: encode62.timeencode(o.key), value: encode62.timeencode("") }).loading_promise;
@@ -1,5 +1,5 @@
1
1
  <div ng-class="{even:i%2===1,odd:i%2===0}" v-for="(r,i) in rates" hz:='r[5]'>
2
- <span ng-if="i%2===0" v-bind="i/2 -2"></span>
2
+ <span ng-if="i%2===0" v-bind="i/2 -8"></span>
3
3
  <button ng-repeat="o in r" _rate=o>
4
4
  <span ng-bind="format(o)"></span>
5
5
  </button>
@@ -2,8 +2,8 @@ function piano() {
2
2
  var res = [];
3
3
  var yin = [1, 3, 5, 7, 8, 10, 12].reverse();
4
4
  var yue = [2, 4, 6, 9, 11].reverse();
5
- var c=a => 440 * Math.pow(2, cx + (3 - a) / 12);
6
- for (var cx = -6, dx = 8; cx < dx; cx++) {
5
+ var c = a => 440 * Math.pow(2, cx + (3 - a) / 12);
6
+ for (var cx = -12, dx = 8; cx < dx; cx++) {
7
7
  var yinjie1 = yin.map(c);
8
8
  var yinjie2 = yue.map(c);
9
9
  res.push(yinjie1, yinjie2);
@@ -23,7 +23,7 @@ function main() {
23
23
  oscillator.connect(gainNode);
24
24
  gainNode.connect(audioCtx.destination);
25
25
  oscillator.type = 'sine';
26
- oscillator.frequency.value = hz;
26
+ oscillator.frequency.value = -hz;
27
27
  gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
28
28
  gainNode.gain.linearRampToValueAtTime(65536 / Math.log2(hz), audioCtx.currentTime + 0.01);
29
29
  oscillator.start(audioCtx.currentTime);
@@ -1,10 +1,12 @@
1
1
  function bind(eventName, bindTo = window) {
2
2
  return function (target, eventListener) {
3
3
  var off;
4
- var off1 = on("append")(target, function () {
4
+ var mount = function () {
5
5
  if (off) off();
6
6
  off = on(eventName).call(bindTo, target, eventListener);
7
- });
7
+ };
8
+ if (isMounted(target)) mount();
9
+ var off1 = on("append")(target, mount);
8
10
  var off2 = on("remove")(target, function () {
9
11
  if (off) off();
10
12
  off = null;
@@ -1,5 +1,5 @@
1
1
  var cloneProperties = "fontWeight,fontSize,fontFamily,color,textShadow,opacity,writingMode,blockSize,wordSpacing,letterSpacing,whiteSpace".split(",");
2
- var cloneProperties2 = "position,float,clear,margin,color,verticalAlign,textAlign,textShadow,opacity,boxShadow,overflow,writingMode,blockSize,wordSpacing,letterSpacing,textIndent,lineHeight,display,appearance,webkitAppearance,MozAppearance".split(",");
2
+ var cloneProperties2 = "position,backdropFilter,float,clear,margin,color,verticalAlign,textAlign,textShadow,opacity,boxShadow,overflow,writingMode,blockSize,wordSpacing,letterSpacing,textIndent,lineHeight,display,appearance,webkitAppearance,MozAppearance".split(",");
3
3
  var pushProperty = function (key, props) {
4
4
  props.split(",").forEach(k => {
5
5
  cloneProperties2.push(key + k);
@@ -86,7 +86,13 @@ var isMaybeVisible = function (node) {
86
86
  if (style.overflow === "hidden") {
87
87
  if (node.offsetHeight === 0 || node.offsetWidth === 0) return;
88
88
  }
89
- if (node.offsetParent && !overlap(node, node.offsetParent)) return;
89
+ if (node.offsetParent) {
90
+ var parent = node.offsetParent;
91
+ return !(node.offsetLeft + node.offsetWidth - parent.scrollLeft <= parent.clientLeft ||
92
+ node.offsetTop + node.offsetHeight - parent.scrollTop <= parent.clientTop ||
93
+ node.offsetLeft - parent.scrollLeft >= (parent.clientWidth || parent.offsetWidth) ||
94
+ node.offsetTop - parent.scrollTop >= (parent.clientHeight || parent.offsetHeight));
95
+ }
90
96
  return true;
91
97
  }
92
98
  var clonePseudo = function (node, pseudo) {
@@ -173,6 +179,7 @@ var cloneVisible = function (td) {
173
179
  };
174
180
  clone(td);
175
181
  extend(result.style, {
182
+ zIndex: getComputedStyle(td).zIndex,
176
183
  position: "absolute",
177
184
  left: _left + "px",
178
185
  top: _top + "px",
@@ -17,9 +17,9 @@ if (cookiesData) {
17
17
  }
18
18
  }
19
19
 
20
- var digest = function () {
20
+ var digest = lazy(function () {
21
21
  dispatch('render', window);
22
- };
22
+ }, 0);
23
23
 
24
24
  var cross = cross_.bind(function (callback, onerror) {
25
25
  var xhr = new XMLHttpRequest;
@@ -32,7 +32,6 @@ var cross = cross_.bind(function (callback, onerror) {
32
32
 
33
33
  xhr.onreadystatechange = function () {
34
34
  if (xhr.readyState === 4) {
35
- dispatch(window, 'render');
36
35
  switch (xhr.status) {
37
36
  case 0:
38
37
  if (!navigator.onLine) {
@@ -75,8 +75,24 @@ const formulaters = {
75
75
  var seekFromSource = function (obj, base) {
76
76
  var source = dataSourceMap;
77
77
  if (base && base in dataSourceMap) source = source[base];
78
- obj = seek(source, obj);
79
- if (isObject(obj)) for (var k in obj) if (obj[k] === dataSourceMap) delete obj[k];
78
+ if (isObject(obj)) {
79
+ var dist = {};
80
+ for (var k in obj) {
81
+ var v = obj[k];
82
+ if (/^\:/.test(k)) {
83
+ k = seek(source, k.slice(1));
84
+ }
85
+ if (v === '') {
86
+ if (source !== dataSourceMap) v = source;
87
+ else v = seek(source, k);
88
+ }
89
+ else if (/^\:/.test(v)) {
90
+ v = seek(source, v.slice(1));
91
+ }
92
+ dist[k] = v;
93
+ }
94
+ obj = dist;
95
+ }
80
96
  return obj;
81
97
  };
82
98
  function getErrorMessage(error = this) {
@@ -553,8 +569,10 @@ var privates = {
553
569
  headers = seekFromSource(headers, api.base);
554
570
  }
555
571
  cross(realmethod, uri, headers).send(params).done(e => {
572
+ updateLoadingCount();
556
573
  ok(e.response || e.responseText);
557
574
  }).error(xhr => {
575
+ updateLoadingCount();
558
576
  try {
559
577
  var e = getErrorMessage(parseData(xhr.response || xhr.responseText || xhr.statusText || xhr.status));
560
578
  oh({ status: xhr.status, api, params: params1, error: e, toString: getErrorMessage })
@@ -627,7 +645,6 @@ function responseCrash(e, data) {
627
645
  data.error = e;
628
646
  }
629
647
  error_report(e, e.status < 500 ? 'warn' : 'error');
630
- updateLoadingCount();
631
648
  }
632
649
  var getData = function () { return this.data };
633
650
  var updateLoadingCount = function () {
@@ -643,7 +660,6 @@ var data = {
643
660
  response.is_loading = false;
644
661
  if (response.then === LoadingArray_then) delete response.then;
645
662
  }
646
- updateLoadingCount();
647
663
  },
648
664
  responseCrash,
649
665
  responseLoading(response) {
@@ -865,10 +881,12 @@ var data = {
865
881
  headers = seekFromSource(headers, api.base);
866
882
  }
867
883
  instance.loading = cross(method, uri, headers).send(params).done(xhr => {
884
+ updateLoadingCount();
868
885
  if (instance.loading !== xhr) return oh(aborted);
869
886
  instance.loading = null;
870
887
  ok(xhr.responseText || xhr.response);
871
888
  }).error(xhr => {
889
+ updateLoadingCount();
872
890
  if (instance.loading !== xhr) return oh(aborted);
873
891
  instance.loading = null;
874
892
  try {
@@ -61,8 +61,9 @@ function drag(target, initialEvent, preventOverflow, isMovingSource) {
61
61
  saved_delta.x += clone_left - target_left;
62
62
  saved_delta.y += clone_top - target_top;
63
63
  if (clone.style) {
64
- clone.style.zIndex = zIndex();
65
- extraClones.map(e => e.style.zIndex = clone.style.zIndex);
64
+ var z = zIndex();
65
+ clone.style.zIndex = z + (+clone.style.zIndex || 0);
66
+ extraClones.map(e => e.style.zIndex = z + (+e.style.zIndex || 0));
66
67
  }
67
68
  }
68
69
  drag.target = clone;
@@ -1,11 +1,16 @@
1
- <div class="head">
2
- <span ng-bind=field.name></span>
3
- <span class="required" ng-if="field.required">*</span>
4
- </div>
5
- <div class="body">
1
+ <template v-if='field?.key'>
2
+ <div class="head">
3
+ <span ng-bind=field.name></span>
4
+ <span class="required" ng-if="field.required">*</span>
5
+ </div>
6
+ <div class="body">
7
+ <model _data=data _field=field></model>
8
+ <span class="error" ng-bind="error"></span>
9
+ </div>
10
+ <div class="foot" ng-if="field.msg">
11
+ <span ng-bind=field.msg></span>
12
+ </div>
13
+ </template>
14
+ <template v-else>
6
15
  <model _data=data _field=field></model>
7
- <span class="error" ng-bind="error"></span>
8
- </div>
9
- <div class="foot" ng-if="field.msg">
10
- <span ng-bind=field.msg></span>
11
- </div>
16
+ </template>