n-design-readonly-plugin 1.0.2 → 1.0.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pangsh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -31,14 +31,14 @@ import App from './App.vue';
31
31
 
32
32
  // 引入 n-designv3(必须)
33
33
  import NDesignV3 from 'n-designv3';
34
- import 'n-designv3/dist/style.css';
34
+ import 'n-designv3/dist/nancal.variable.min.css';
35
35
 
36
36
  // 引入本插件
37
- import { NkReadonlyPlugin } from 'nk-readonly-hoc';
37
+ import NDesignReadolyPlugin from 'n-design-readonly-plugin';
38
38
 
39
39
  const app = createApp(App);
40
40
  app.use(NDesignV3);
41
- app.use(NkReadonlyPlugin); // 注册后自动提供 NkInput, NkSelect 等组件
41
+ app.use(NDesignReadolyPlugin); // 注册后自动提供 NkInput, NkSelect 等组件
42
42
  app.mount('#app');
43
43
 
44
44
  ```
package/dist/index.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("vue"),l=require("n-designv3");function x(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(x).join("");if(typeof e=="object"){if(typeof e.children=="string")return e.children;if(e?.default&&typeof e.default=="function"){const n=e.default();if(Array.isArray(n))return x(n)}e.children&&x(e.children)}return""}function L(e,n={}){const t=new Map,{value:c="value",label:s="label"}=n;if(!e)return t;const d=Array.isArray(e)?e:[e];for(const r of d)if(!(!r||typeof r!="object")){if(r.type&&(typeof r.type=="object"||typeof r.type=="function")){let f=r.type.name?.toLowerCase()||"";if(typeof r.type=="function"?f=r.type.displayName?.toLowerCase()||"":typeof r.type=="string"&&(f=r.type?.toLowerCase()||""),/option|radio|checkbox/i.test(f)){const o=r.props||{},a=o[c]??o.value,y=o[s]??o.label;if(a!=null){const w=x(r.children)||y||String(a);t.set(a,w)}}}r.children&&L(r.children,n).forEach((o,a)=>{t.has(a)||t.set(a,o)})}return t}function G(e,n,t){const{value:c="value",label:s="label",children:d="children"}=t,r=[];let f=e;for(const o of n){const a=f.find(y=>y[c]===o);if(a)r.push(a[s]||a[c]||String(o)),f=a[d]||[];else{r.push(String(o));break}}return r}function D(e,n,t){const{value:c="value",label:s="title",children:d="children"}=t,r=[],f=new Set(Array.isArray(n)?n:[n]);function o(a){for(const y of a)f.has(y[c])&&r.push(y[s]||y[c]),y[d]&&o(y[d])}return o(e),r}function H({modelValue:e,options:n,treeData:t,valueToLabel:c,fieldNames:s,slots:d,isSelect:r,isRadioGroup:f,isCheckboxGroup:o,isCheckbox:a,isRadio:y,isCascader:w,isTreeSelect:C,isSwitch:T,attrs:p,emptyText:k}){if(c)try{return c(e)||k}catch(i){console.warn("[ReadonlyHOC] valueToLabel error",i)}if(T){const i=p.checkedValue??p["checked-value"]??!0,h=p.uncheckedValue??p["un-checked-value"]??!1;return e===i?p.checkedChildren??p["checked-children"]??"开启":e===h?p.uncheckedChildren??p["un-checked-children"]??"关闭":String(e||k)}if(w&&Array.isArray(e)&&n?.length)return G(n,e,s).join(" / ")||k;if(C&&t?.length)return D(t,e,s).join(", ")||k;if(n?.length){const i=s.value||"value",h=s.label||"label",S=v=>{const R=n.find(m=>m[i]===v);return R?R[h]||R[i]:v==null?"":String(v)};return Array.isArray(e)?e.map(S).join(", ")||k:S(e)||k}else if((r||f||o)&&d?.default)try{const i=d.default(),h=L(i,s);if(h.size>0){const S=v=>v==null?"":h.get(v)||String(v);return Array.isArray(e)?e.map(S).join(", ")||k:S(e)||k}}catch(i){console.warn("[ReadonlyHOC] Failed to parse slot options",i)}else if(d?.default&&(a||y)&&typeof e=="boolean")try{const i=d.default(),h=x(i);if(h)return h}catch{}return e==null||e===""?k:Array.isArray(e)?e.join(", "):String(e)}function O(e){const n=e.name||"",t=n.toLowerCase(),c=/select/i.test(t)&&!/tree|cascader/i.test(t),s=/radio/i.test(t)&&!/group/i.test(t),d=/radio.*group/i.test(t),r=/checkbox/i.test(t)&&!/group/i.test(t),f=/checkbox.*group/i.test(t),o=/switch/i.test(t),a=/cascader/i.test(t),y=/tree.*select/i.test(t),C=r||o||s?"checked":"value",T=`update:${C}`,p=/form$/i.test(t),k=/form.*item/i.test(t);return u.defineComponent({name:n,inheritAttrs:!1,props:{modelValue:{type:[String,Number,Boolean,Array,Object],default:void 0},[C]:{type:[String,Number,Boolean,Array,Object],default:void 0},readonly:{type:Boolean,default:void 0},emptyText:{type:String,default:"--"},valueToLabel:{type:Function,default:null}},emits:["update:modelValue",T],setup(i,{emit:h,slots:S,expose:v}){const R=u.inject("nkReadonly",u.ref(!1)),m=u.computed(()=>i.readonly??R.value),F=u.computed(()=>i.readonly??R.value);p&&u.provide("nkReadonly",F);const j=u.computed(()=>i[C]!==void 0?i[C]:i.modelValue),P=g=>{h("update:modelValue",g),h(T,g)},b=u.useAttrs(),K=u.computed(()=>{if(!m.value)return"";const g=b.fieldNames??b["field-names"]??{},M=b.options,I=b.treeData??b["tree-data"];return H({modelValue:j.value,options:M,treeData:I,valueToLabel:i.valueToLabel,fieldNames:g,slots:S,isSelect:c,isRadioGroup:d,isCheckboxGroup:f,isCheckbox:r,isRadio:s,isCascader:a,isTreeSelect:y,isSwitch:o,attrs:b,emptyText:i.emptyText})}),N={lineHeight:"32px",padding:"0 6px",display:"inline-block",minHeight:"32px",border:"none",backgroundColor:"none",cursor:"default",wordBreak:"break-all",color:"rgba(0, 0, 0, 0.65)"},A=u.ref();return p&&v({validate:()=>m.value?Promise.resolve({}):A.value?.validate?.(),validateFields:g=>m.value?Promise.resolve({}):A.value?.validateFields?.(g),resetFields:g=>!m.value&&A.value?.resetFields?.(g),clearValidate:g=>!m.value&&A.value?.clearValidate?.(g),scrollToField:g=>!m.value&&A.value?.scrollToField?.(g)}),()=>k?u.h(e,{...b,style:{...b.style||{},marginBottom:m.value?"10px":void 0}},S):p?u.h(e,{...b,rules:F.value?null:b.rules,disabled:F.value?!0:b.disabled,ref:A},S):m.value?u.h("span",{class:"nk-readonly-wrapper",style:N,role:"text",tabindex:0,"aria-readonly":"true"},K.value):u.h(e,{...b,[C]:j.value,[`onUpdate:${C}`]:P,ref:p?A:void 0},S)}})}const $={Input:l.Input,InputNumber:l.InputNumber,Textarea:l.Textarea,Select:l.Select,SelectOption:l.Select.Option,TreeSelect:l.TreeSelect,Cascader:l.Cascader,DatePicker:l.DatePicker,TimePicker:l.TimePicker,Radio:l.Radio,Checkbox:l.Checkbox,RadioGroup:l.Radio.Group,CheckboxGroup:l.Checkbox.Group,Switch:l.Switch,Form:l.Form,FormItem:l.Form.Item},E={install(e){for(const[n,t]of Object.entries($))if(t){const c=O(t);e.component(`Nk${n}`,c)}}};exports.NkReadonlyPlugin=E;exports.withReadonly=O;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const u=require("vue"),l=require("n-designv3");function R(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(R).join("");if(typeof e=="object"){if(typeof e.children=="string")return e.children;if(e?.default&&typeof e.default=="function"){const n=e.default();if(Array.isArray(n))return R(n)}e.children&&R(e.children)}return""}function L(e,n={}){const t=new Map,{value:c="value",label:s="label"}=n;if(!e)return t;const d=Array.isArray(e)?e:[e];for(const r of d)if(!(!r||typeof r!="object")){if(r.type&&(typeof r.type=="object"||typeof r.type=="function")){let f=r.type.name?.toLowerCase()||"";if(typeof r.type=="function"?f=r.type.displayName?.toLowerCase()||"":typeof r.type=="string"&&(f=r.type?.toLowerCase()||""),/option|radio|checkbox/i.test(f)){const o=r.props||{},a=o[c]??o.value,y=o[s]??o.label;if(a!=null){const w=R(r.children)||y||String(a);t.set(a,w)}}}r.children&&L(r.children,n).forEach((o,a)=>{t.has(a)||t.set(a,o)})}return t}function D(e,n,t){const{value:c="value",label:s="label",children:d="children"}=t,r=[];let f=e;for(const o of n){const a=f.find(y=>y[c]===o);if(a)r.push(a[s]||a[c]||String(o)),f=a[d]||[];else{r.push(String(o));break}}return r}function G(e,n,t){const{value:c="value",label:s="title",children:d="children"}=t,r=[],f=new Set(Array.isArray(n)?n:[n]);function o(a){for(const y of a)f.has(y[c])&&r.push(y[s]||y[c]),y[d]&&o(y[d])}return o(e),r}function H({modelValue:e,options:n,treeData:t,valueToLabel:c,fieldNames:s,slots:d,isSelect:r,isRadioGroup:f,isCheckboxGroup:o,isCheckbox:a,isRadio:y,isCascader:w,isTreeSelect:C,isSwitch:T,attrs:p,emptyText:g}){if(c)try{return c(e)||g}catch(i){console.warn("[ReadonlyHOC] valueToLabel error",i)}if(T){const i=p.checkedValue??p["checked-value"]??!0,h=p.uncheckedValue??p["un-checked-value"]??!1;return e===i?p.checkedChildren??p["checked-children"]??"开启":e===h?p.uncheckedChildren??p["un-checked-children"]??"关闭":String(e||g)}if(w&&Array.isArray(e)&&n?.length)return D(n,e,s).join(" / ")||g;if(C&&t?.length)return G(t,e,s).join(", ")||g;if(n?.length){const i=s.value||"value",h=s.label||"label",S=k=>{const x=n.find(m=>m[i]===k);return x?x[h]||x[i]:k==null?"":String(k)};return Array.isArray(e)?e.map(S).join(", ")||g:S(e)||g}else if((r||f||o)&&d?.default)try{const i=d.default(),h=L(i,s);if(h.size>0){const S=k=>k==null?"":h.get(k)||String(k);return Array.isArray(e)?e.map(S).join(", ")||g:S(e)||g}}catch(i){console.warn("[ReadonlyHOC] Failed to parse slot options",i)}else if(d?.default&&(a||y)&&typeof e=="boolean")try{const i=d.default(),h=R(i);if(h)return h}catch{}return e==null||e===""?g:Array.isArray(e)?e.join(", "):String(e)}function O(e){if(typeof e=="function")return e;const n=e.name||"",t=n.toLowerCase(),c=/select/i.test(t)&&!/tree|cascader/i.test(t),s=/radio/i.test(t)&&!/group/i.test(t),d=/radio.*group/i.test(t),r=/checkbox/i.test(t)&&!/group/i.test(t),f=/checkbox.*group/i.test(t),o=/switch/i.test(t),a=/cascader/i.test(t),y=/tree.*select/i.test(t),C=r||o||s?"checked":"value",T=`update:${C}`,p=/form$/i.test(t),g=/form.*item/i.test(t);return u.defineComponent({name:n,inheritAttrs:!1,props:{modelValue:{type:[String,Number,Boolean,Array,Object],default:void 0},[C]:{type:[String,Number,Boolean,Array,Object],default:void 0},readonly:{type:Boolean,default:void 0},emptyText:{type:String,default:"--"},valueToLabel:{type:Function,default:null}},emits:["update:modelValue",T],setup(i,{emit:h,slots:S,expose:k}){const x=u.inject("nkReadonly",u.ref(!1)),m=u.computed(()=>i.readonly??x.value),F=u.computed(()=>i.readonly??x.value);p&&u.provide("nkReadonly",F);const j=u.computed(()=>i[C]!==void 0?i[C]:i.modelValue),K=v=>{h("update:modelValue",v),h(T,v)},b=u.useAttrs(),P=u.computed(()=>{if(!m.value)return"";const v=b.fieldNames??b["field-names"]??{},N=b.options,I=b.treeData??b["tree-data"];return H({modelValue:j.value,options:N,treeData:I,valueToLabel:i.valueToLabel,fieldNames:v,slots:S,isSelect:c,isRadioGroup:d,isCheckboxGroup:f,isCheckbox:r,isRadio:s,isCascader:a,isTreeSelect:y,isSwitch:o,attrs:b,emptyText:i.emptyText})}),M={lineHeight:"32px",padding:"0 6px",display:"inline-block",minHeight:"32px",border:"none",backgroundColor:"none",cursor:"default",wordBreak:"break-all",color:"rgba(0, 0, 0, 0.65)"},A=u.ref();return p&&k({validate:()=>m.value?Promise.resolve({}):A.value?.validate?.(),validateFields:v=>m.value?Promise.resolve({}):A.value?.validateFields?.(v),resetFields:v=>!m.value&&A.value?.resetFields?.(v),clearValidate:v=>!m.value&&A.value?.clearValidate?.(v),scrollToField:v=>!m.value&&A.value?.scrollToField?.(v)}),()=>g?u.h(e,{...b,style:{...b.style||{},marginBottom:m.value?"10px":void 0}},S):p?u.h(e,{...b,rules:F.value?null:b.rules,disabled:F.value?!0:b.disabled,ref:A},S):m.value?u.h("span",{class:"nk-readonly-wrapper",style:M,role:"text",tabindex:0,"aria-readonly":"true"},P.value):u.h(e,{...b,[C]:j.value,[`onUpdate:${C}`]:K,ref:p?A:void 0},S)}})}const $={Input:l.Input,InputNumber:l.InputNumber,Textarea:l.Textarea,Select:l.Select,SelectOption:l.Select.Option,TreeSelect:l.TreeSelect,Cascader:l.Cascader,DatePicker:l.DatePicker,TimePicker:l.TimePicker,Radio:l.Radio,Checkbox:l.Checkbox,RadioGroup:l.Radio.Group,CheckboxGroup:l.Checkbox.Group,Switch:l.Switch,Form:l.Form,FormItem:l.Form.Item},E={install(e){for(const[n,t]of Object.entries($))if(t){const c=O(t);e.component(`Nk${n}`,c)}}};exports.default=E;exports.withReadonly=O;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Plugin } from 'vue';
2
2
  import { withReadonly } from './withReadonly';
3
- export declare const NkReadonlyPlugin: Plugin;
3
+ declare const NDesignReadolyPlugin: Plugin;
4
+ export default NDesignReadolyPlugin;
4
5
  export { withReadonly };
package/dist/index.es.js CHANGED
@@ -1,18 +1,18 @@
1
- import { defineComponent as E, inject as B, ref as T, computed as w, provide as U, useAttrs as z, h as j } from "vue";
2
- import { Form as K, Checkbox as O, Radio as N, Select as M, Switch as q, TimePicker as J, DatePicker as Q, Cascader as W, TreeSelect as X, Textarea as Y, InputNumber as Z, Input as _ } from "n-designv3";
3
- function A(e) {
1
+ import { defineComponent as E, inject as U, ref as T, computed as w, provide as z, useAttrs as q, h as j } from "vue";
2
+ import { Form as K, Checkbox as O, Radio as N, Select as M, Switch as B, TimePicker as J, DatePicker as Q, Cascader as W, TreeSelect as X, Textarea as Y, InputNumber as Z, Input as _ } from "n-designv3";
3
+ function C(e) {
4
4
  if (e == null) return "";
5
5
  if (typeof e == "string") return e;
6
- if (Array.isArray(e)) return e.map(A).join("");
6
+ if (Array.isArray(e)) return e.map(C).join("");
7
7
  if (typeof e == "object") {
8
8
  if (typeof e.children == "string")
9
9
  return e.children;
10
10
  if (e?.default && typeof e.default == "function") {
11
11
  const n = e.default();
12
12
  if (Array.isArray(n))
13
- return A(n);
13
+ return C(n);
14
14
  }
15
- e.children && A(e.children);
15
+ e.children && C(e.children);
16
16
  }
17
17
  return "";
18
18
  }
@@ -25,15 +25,15 @@ function P(e, n = {}) {
25
25
  if (r.type && (typeof r.type == "object" || typeof r.type == "function")) {
26
26
  let u = r.type.name?.toLowerCase() || "";
27
27
  if (typeof r.type == "function" ? u = r.type.displayName?.toLowerCase() || "" : typeof r.type == "string" && (u = r.type?.toLowerCase() || ""), /option|radio|checkbox/i.test(u)) {
28
- const o = r.props || {}, a = o[l] ?? o.value, f = o[c] ?? o.label;
29
- if (a != null) {
30
- const x = A(r.children) || f || String(a);
31
- t.set(a, x);
28
+ const a = r.props || {}, o = a[l] ?? a.value, f = a[c] ?? a.label;
29
+ if (o != null) {
30
+ const x = C(r.children) || f || String(o);
31
+ t.set(o, x);
32
32
  }
33
33
  }
34
34
  }
35
- r.children && P(r.children, n).forEach((o, a) => {
36
- t.has(a) || t.set(a, o);
35
+ r.children && P(r.children, n).forEach((a, o) => {
36
+ t.has(o) || t.set(o, a);
37
37
  });
38
38
  }
39
39
  return t;
@@ -41,12 +41,12 @@ function P(e, n = {}) {
41
41
  function V(e, n, t) {
42
42
  const { value: l = "value", label: c = "label", children: s = "children" } = t, r = [];
43
43
  let u = e;
44
- for (const o of n) {
45
- const a = u.find((f) => f[l] === o);
46
- if (a)
47
- r.push(a[c] || a[l] || String(o)), u = a[s] || [];
44
+ for (const a of n) {
45
+ const o = u.find((f) => f[l] === a);
46
+ if (o)
47
+ r.push(o[c] || o[l] || String(a)), u = o[s] || [];
48
48
  else {
49
- r.push(String(o));
49
+ r.push(String(a));
50
50
  break;
51
51
  }
52
52
  }
@@ -54,11 +54,11 @@ function V(e, n, t) {
54
54
  }
55
55
  function ee(e, n, t) {
56
56
  const { value: l = "value", label: c = "title", children: s = "children" } = t, r = [], u = new Set(Array.isArray(n) ? n : [n]);
57
- function o(a) {
58
- for (const f of a)
59
- u.has(f[l]) && r.push(f[c] || f[l]), f[s] && o(f[s]);
57
+ function a(o) {
58
+ for (const f of o)
59
+ u.has(f[l]) && r.push(f[c] || f[l]), f[s] && a(f[s]);
60
60
  }
61
- return o(e), r;
61
+ return a(e), r;
62
62
  }
63
63
  function te({
64
64
  modelValue: e,
@@ -69,11 +69,11 @@ function te({
69
69
  slots: s,
70
70
  isSelect: r,
71
71
  isRadioGroup: u,
72
- isCheckboxGroup: o,
73
- isCheckbox: a,
72
+ isCheckboxGroup: a,
73
+ isCheckbox: o,
74
74
  isRadio: f,
75
75
  isCascader: x,
76
- isTreeSelect: m,
76
+ isTreeSelect: S,
77
77
  isSwitch: R,
78
78
  attrs: d,
79
79
  emptyText: b
@@ -90,72 +90,74 @@ function te({
90
90
  }
91
91
  if (x && Array.isArray(e) && n?.length)
92
92
  return V(n, e, c).join(" / ") || b;
93
- if (m && t?.length)
93
+ if (S && t?.length)
94
94
  return ee(t, e, c).join(", ") || b;
95
95
  if (n?.length) {
96
- const i = c.value || "value", y = c.label || "label", g = (v) => {
97
- const C = n.find((k) => k[i] === v);
98
- return C ? C[y] || C[i] : v == null ? "" : String(v);
96
+ const i = c.value || "value", y = c.label || "label", v = (g) => {
97
+ const A = n.find((k) => k[i] === g);
98
+ return A ? A[y] || A[i] : g == null ? "" : String(g);
99
99
  };
100
- return Array.isArray(e) ? e.map(g).join(", ") || b : g(e) || b;
101
- } else if ((r || u || o) && s?.default)
100
+ return Array.isArray(e) ? e.map(v).join(", ") || b : v(e) || b;
101
+ } else if ((r || u || a) && s?.default)
102
102
  try {
103
103
  const i = s.default(), y = P(i, c);
104
104
  if (y.size > 0) {
105
- const g = (v) => v == null ? "" : y.get(v) || String(v);
106
- return Array.isArray(e) ? e.map(g).join(", ") || b : g(e) || b;
105
+ const v = (g) => g == null ? "" : y.get(g) || String(g);
106
+ return Array.isArray(e) ? e.map(v).join(", ") || b : v(e) || b;
107
107
  }
108
108
  } catch (i) {
109
109
  console.warn("[ReadonlyHOC] Failed to parse slot options", i);
110
110
  }
111
- else if (s?.default && (a || f) && typeof e == "boolean")
111
+ else if (s?.default && (o || f) && typeof e == "boolean")
112
112
  try {
113
- const i = s.default(), y = A(i);
113
+ const i = s.default(), y = C(i);
114
114
  if (y) return y;
115
115
  } catch {
116
116
  }
117
117
  return e == null || e === "" ? b : Array.isArray(e) ? e.join(", ") : String(e);
118
118
  }
119
119
  function re(e) {
120
- const n = e.name || "", t = n.toLowerCase(), l = /select/i.test(t) && !/tree|cascader/i.test(t), c = /radio/i.test(t) && !/group/i.test(t), s = /radio.*group/i.test(t), r = /checkbox/i.test(t) && !/group/i.test(t), u = /checkbox.*group/i.test(t), o = /switch/i.test(t), a = /cascader/i.test(t), f = /tree.*select/i.test(t), m = r || o || c ? "checked" : "value", R = `update:${m}`, d = /form$/i.test(t), b = /form.*item/i.test(t);
120
+ if (typeof e == "function")
121
+ return e;
122
+ const n = e.name || "", t = n.toLowerCase(), l = /select/i.test(t) && !/tree|cascader/i.test(t), c = /radio/i.test(t) && !/group/i.test(t), s = /radio.*group/i.test(t), r = /checkbox/i.test(t) && !/group/i.test(t), u = /checkbox.*group/i.test(t), a = /switch/i.test(t), o = /cascader/i.test(t), f = /tree.*select/i.test(t), S = r || a || c ? "checked" : "value", R = `update:${S}`, d = /form$/i.test(t), b = /form.*item/i.test(t);
121
123
  return E({
122
124
  name: n,
123
125
  inheritAttrs: !1,
124
126
  props: {
125
127
  modelValue: { type: [String, Number, Boolean, Array, Object], default: void 0 },
126
- [m]: { type: [String, Number, Boolean, Array, Object], default: void 0 },
128
+ [S]: { type: [String, Number, Boolean, Array, Object], default: void 0 },
127
129
  readonly: { type: Boolean, default: void 0 },
128
130
  emptyText: { type: String, default: "--" },
129
131
  valueToLabel: { type: Function, default: null }
130
132
  },
131
133
  emits: ["update:modelValue", R],
132
- setup(i, { emit: y, slots: g, expose: v }) {
133
- const C = B("nkReadonly", T(!1)), k = w(() => i.readonly ?? C.value), F = w(() => i.readonly ?? C.value);
134
- d && U("nkReadonly", F);
135
- const L = w(() => i[m] !== void 0 ? i[m] : i.modelValue), G = (h) => {
134
+ setup(i, { emit: y, slots: v, expose: g }) {
135
+ const A = U("nkReadonly", T(!1)), k = w(() => i.readonly ?? A.value), F = w(() => i.readonly ?? A.value);
136
+ d && z("nkReadonly", F);
137
+ const L = w(() => i[S] !== void 0 ? i[S] : i.modelValue), G = (h) => {
136
138
  y("update:modelValue", h), y(R, h);
137
- }, p = z(), I = w(() => {
139
+ }, p = q(), I = w(() => {
138
140
  if (!k.value) return "";
139
- const h = p.fieldNames ?? p["field-names"] ?? {}, $ = p.options, D = p.treeData ?? p["tree-data"];
141
+ const h = p.fieldNames ?? p["field-names"] ?? {}, H = p.options, $ = p.treeData ?? p["tree-data"];
140
142
  return te({
141
143
  modelValue: L.value,
142
- options: $,
143
- treeData: D,
144
+ options: H,
145
+ treeData: $,
144
146
  valueToLabel: i.valueToLabel,
145
147
  fieldNames: h,
146
- slots: g,
148
+ slots: v,
147
149
  isSelect: l,
148
150
  isRadioGroup: s,
149
151
  isCheckboxGroup: u,
150
152
  isCheckbox: r,
151
153
  isRadio: c,
152
- isCascader: a,
154
+ isCascader: o,
153
155
  isTreeSelect: f,
154
- isSwitch: o,
156
+ isSwitch: a,
155
157
  attrs: p,
156
158
  emptyText: i.emptyText
157
159
  });
158
- }), H = {
160
+ }), D = {
159
161
  lineHeight: "32px",
160
162
  padding: "0 6px",
161
163
  display: "inline-block",
@@ -165,34 +167,34 @@ function re(e) {
165
167
  cursor: "default",
166
168
  wordBreak: "break-all",
167
169
  color: "rgba(0, 0, 0, 0.65)"
168
- }, S = T();
169
- return d && v({
170
- validate: () => k.value ? Promise.resolve({}) : S.value?.validate?.(),
171
- validateFields: (h) => k.value ? Promise.resolve({}) : S.value?.validateFields?.(h),
172
- resetFields: (h) => !k.value && S.value?.resetFields?.(h),
173
- clearValidate: (h) => !k.value && S.value?.clearValidate?.(h),
174
- scrollToField: (h) => !k.value && S.value?.scrollToField?.(h)
170
+ }, m = T();
171
+ return d && g({
172
+ validate: () => k.value ? Promise.resolve({}) : m.value?.validate?.(),
173
+ validateFields: (h) => k.value ? Promise.resolve({}) : m.value?.validateFields?.(h),
174
+ resetFields: (h) => !k.value && m.value?.resetFields?.(h),
175
+ clearValidate: (h) => !k.value && m.value?.clearValidate?.(h),
176
+ scrollToField: (h) => !k.value && m.value?.scrollToField?.(h)
175
177
  }), () => b ? j(
176
178
  e,
177
179
  {
178
180
  ...p,
179
181
  style: { ...p.style || {}, marginBottom: k.value ? "10px" : void 0 }
180
182
  },
181
- g
183
+ v
182
184
  ) : d ? j(
183
185
  e,
184
186
  {
185
187
  ...p,
186
188
  rules: F.value ? null : p.rules,
187
189
  disabled: F.value ? !0 : p.disabled,
188
- ref: S
190
+ ref: m
189
191
  },
190
- g
192
+ v
191
193
  ) : k.value ? j(
192
194
  "span",
193
195
  {
194
196
  class: "nk-readonly-wrapper",
195
- style: H,
197
+ style: D,
196
198
  role: "text",
197
199
  tabindex: 0,
198
200
  "aria-readonly": "true"
@@ -202,11 +204,11 @@ function re(e) {
202
204
  e,
203
205
  {
204
206
  ...p,
205
- [m]: L.value,
206
- [`onUpdate:${m}`]: G,
207
- ref: d ? S : void 0
207
+ [S]: L.value,
208
+ [`onUpdate:${S}`]: G,
209
+ ref: d ? m : void 0
208
210
  },
209
- g
211
+ v
210
212
  );
211
213
  }
212
214
  });
@@ -225,10 +227,10 @@ const ne = {
225
227
  Checkbox: O,
226
228
  RadioGroup: N.Group,
227
229
  CheckboxGroup: O.Group,
228
- Switch: q,
230
+ Switch: B,
229
231
  Form: K,
230
232
  FormItem: K.Item
231
- }, ae = {
233
+ }, oe = {
232
234
  install(e) {
233
235
  for (const [n, t] of Object.entries(ne))
234
236
  if (t) {
@@ -238,6 +240,6 @@ const ne = {
238
240
  }
239
241
  };
240
242
  export {
241
- ae as NkReadonlyPlugin,
243
+ oe as default,
242
244
  re as withReadonly
243
245
  };
@@ -1,71 +1,7 @@
1
- import { PropType, Component, VNode } from 'vue';
2
- type FormModelValue = string | number | boolean | any[] | null | undefined;
3
- type ValueToLabelFn = (value: FormModelValue) => string;
4
- export declare function withReadonly(BaseComponent: Component): import('vue').DefineComponent<{
5
- [x: string]: {
6
- type: PropType<FormModelValue>;
7
- default: undefined;
8
- } | {
9
- type: BooleanConstructor;
10
- default: undefined;
11
- } | {
12
- type: StringConstructor;
13
- default: string;
14
- } | {
15
- type: PropType<ValueToLabelFn>;
16
- default: null;
17
- };
18
- modelValue: {
19
- type: PropType<FormModelValue>;
20
- default: undefined;
21
- };
22
- readonly: {
23
- type: BooleanConstructor;
24
- default: undefined;
25
- };
26
- emptyText: {
27
- type: StringConstructor;
28
- default: string;
29
- };
30
- valueToLabel: {
31
- type: PropType<ValueToLabelFn>;
32
- default: null;
33
- };
34
- }, () => VNode<import('vue').RendererNode, import('vue').RendererElement, {
35
- [key: string]: any;
36
- }>, unknown, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, string[], string, import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, Readonly<import('vue').ExtractPropTypes<{
37
- [x: string]: {
38
- type: PropType<FormModelValue>;
39
- default: undefined;
40
- } | {
41
- type: BooleanConstructor;
42
- default: undefined;
43
- } | {
44
- type: StringConstructor;
45
- default: string;
46
- } | {
47
- type: PropType<ValueToLabelFn>;
48
- default: null;
49
- };
50
- modelValue: {
51
- type: PropType<FormModelValue>;
52
- default: undefined;
53
- };
54
- readonly: {
55
- type: BooleanConstructor;
56
- default: undefined;
57
- };
58
- emptyText: {
59
- type: StringConstructor;
60
- default: string;
61
- };
62
- valueToLabel: {
63
- type: PropType<ValueToLabelFn>;
64
- default: null;
65
- };
66
- }>> & {
67
- [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
68
- }, {
69
- [x: string]: FormModelValue | ValueToLabelFn;
70
- }, {}>;
71
- export {};
1
+ import { Component } from 'vue';
2
+ export declare function withReadonly(BaseComponent: Component): import('vue').FunctionalComponent<any, any, any> | {
3
+ new (...args: any[]): any;
4
+ __isFragment?: never;
5
+ __isTeleport?: never;
6
+ __isSuspense?: never;
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n-design-readonly-plugin",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "只读模式插件 for n-designv3",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.es.js",
@@ -13,8 +13,7 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
- "dist",
17
- ""
16
+ "dist", "README.md", "LICENSE"
18
17
  ],
19
18
  "scripts": {
20
19
  "dev": "vite --config vite.config.playground.ts",
@@ -31,7 +30,7 @@
31
30
  "hoc"
32
31
  ],
33
32
  "author": "pangsh<psh2416623245@163.com>",
34
- "license": "ISC",
33
+ "license": "MIT",
35
34
  "packageManager": "pnpm@10.21.0",
36
35
  "devDependencies": {
37
36
  "@types/node": "^25.2.0",
package/.DS_Store DELETED
Binary file
package/.gitignore DELETED
@@ -1,31 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- pnpm-debug.log*
8
- lerna-debug.log*
9
-
10
- node_modules
11
- .DS_Store
12
- dist
13
- dist-ssr
14
- coverage
15
- *.local
16
- package-lock.json
17
- report.html
18
-
19
- /cypress/videos/
20
- /cypress/screenshots/
21
-
22
- # Editor directories and files
23
- .vscode/*
24
- !.vscode/extensions.json
25
- !.vscode/settings.json
26
- .idea
27
- *.suo
28
- *.ntvs*
29
- *.njsproj
30
- *.sln
31
- *.sw?
Binary file
@@ -1,77 +0,0 @@
1
- <template>
2
- <div class="form-wrapper">
3
- <nk-form :readonly="false" :model="form" :rules="rules">
4
- <nk-form-item label="用户名" name="name">
5
- <nk-input placeholder="用户名" v-model:value="form.name"></nk-input>
6
- </nk-form-item>
7
- <n-row>
8
- <n-col :span="24">
9
- <nk-form-item label="下拉options" name="ep6ClusterObjId">
10
- <nk-select
11
- allowClear
12
- placeholder="请选择"
13
- :options="options"
14
- :showSearch="false"
15
- v-model:value="form.ep6ClusterObjId"
16
- >
17
- </nk-select>
18
- </nk-form-item>
19
- </n-col>
20
- </n-row>
21
- <n-row>
22
- <n-col :span="24">
23
- <nk-form-item label="下拉options" name="ep6ClusterObjId">
24
- <n-select
25
- allowClear
26
- placeholder="请选择"
27
- :options="options"
28
- :showSearch="false"
29
- v-model:value="form.ep6ClusterObjId"
30
- >
31
- </n-select>
32
- </nk-form-item>
33
- </n-col>
34
- </n-row>
35
- </nk-form>
36
- </div>
37
- </template>
38
-
39
- <script lang="ts" setup>
40
- import { reactive } from "vue";
41
-
42
- const form = reactive({
43
- name: "张三",
44
- ep6ClusterObjId: "1",
45
- });
46
- const options = [
47
- {
48
- label: "选项1",
49
- value: "1",
50
- externalValue: "externalValue1",
51
- },
52
- {
53
- label: "选项2",
54
- value: "2",
55
- externalValue: "externalValue2",
56
- },
57
- {
58
- label: "选项3",
59
- value: "3",
60
- externalValue: "externalValue3",
61
- },
62
- ];
63
- const rules = {
64
- name: [{ required: true, message: "请输入用户名" }],
65
- ep6ClusterObjId: [{ required: true, message: "请选择" }],
66
- };
67
- </script>
68
- <style>
69
- .form-wrapper {
70
- width: 100%;
71
- height: 100vh;
72
- padding: 20px;
73
- box-sizing: border-box;
74
- position: relative;
75
- background-color: bisque;
76
- }
77
- </style>
@@ -1,16 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8" />
6
- <link rel="icon" href="/favicon.ico" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
- <title>NkReadonlyHOC Playground</title>
9
- </head>
10
-
11
- <body>
12
- <div id="app"></div>
13
- <script type="module" src="./main.ts"></script>
14
- </body>
15
-
16
- </html>
@@ -1,12 +0,0 @@
1
- // main.ts
2
- import { createApp } from 'vue';
3
- import App from './App.vue';
4
- import { NkReadonlyPlugin } from '../src/index';
5
- import NDesign from 'n-designv3';
6
- import 'n-designv3/dist/nancal.variable.min.css';
7
- import './reset.css'
8
-
9
- const app = createApp(App);
10
- app.use(NDesign);
11
- app.use(NkReadonlyPlugin); // ← 一行注册!
12
- app.mount('#app');
@@ -1,126 +0,0 @@
1
- html,
2
- body,
3
- div,
4
- span,
5
- applet,
6
- object,
7
- iframe,
8
- p,
9
- blockquote,
10
- pre,
11
- a,
12
- abbr,
13
- acronym,
14
- address,
15
- big,
16
- cite,
17
- code,
18
- del,
19
- dfn,
20
- em,
21
- img,
22
- ins,
23
- kbd,
24
- q,
25
- s,
26
- samp,
27
- small,
28
- strike,
29
- strong,
30
- sub,
31
- sup,
32
- tt,
33
- var,
34
- b,
35
- u,
36
- i,
37
- center,
38
- dl,
39
- dt,
40
- dd,
41
- ol,
42
- ul,
43
- li,
44
- fieldset,
45
- form,
46
- label,
47
- legend,
48
- table,
49
- caption,
50
- tbody,
51
- tfoot,
52
- thead,
53
- tr,
54
- th,
55
- td,
56
- article,
57
- aside,
58
- canvas,
59
- details,
60
- embed,
61
- figure,
62
- figcaption,
63
- footer,
64
- header,
65
- hgroup,
66
- menu,
67
- nav,
68
- output,
69
- ruby,
70
- section,
71
- summary,
72
- time,
73
- mark,
74
- audio,
75
- video {
76
- margin: 0;
77
- padding: 0;
78
- border: 0;
79
- font-size: 100%;
80
- font: inherit;
81
- // vertical-align: baseline;
82
- }
83
- /* HTML5 display-role reset for older browsers */
84
- article,
85
- aside,
86
- details,
87
- figcaption,
88
- figure,
89
- footer,
90
- header,
91
- hgroup,
92
- menu,
93
- nav,
94
- section {
95
- display: block;
96
- }
97
- body {
98
- line-height: 1;
99
- }
100
- ol,
101
- ul {
102
- list-style: none;
103
- }
104
- blockquote,
105
- q {
106
- quotes: none;
107
- }
108
- blockquote:before,
109
- blockquote:after,
110
- q:before,
111
- q:after {
112
- content: '';
113
- content: none;
114
- }
115
- table {
116
- border-collapse: collapse;
117
- border-spacing: 0;
118
- }
119
- html,
120
- body {
121
- width: 100%;
122
- height: 100%;
123
- padding: 0;
124
- margin: 0;
125
- font-family: 'PingFang SC-Medium', 'PingFang SC-Regular', 'PingFang SC', 'STHeitiSC-Light', 'Helvetica-Light', 'Arial', 'sans-serif';
126
- }
package/src/index.ts DELETED
@@ -1,52 +0,0 @@
1
- // src/index.ts
2
- import type { App, Plugin } from 'vue';
3
- import { withReadonly } from './withReadonly';
4
-
5
- // 👇 直接从 n-designv3 导入组件(构建时 external,不打包)
6
- import {
7
- Input,
8
- InputNumber,
9
- Textarea,
10
- Select,
11
- TreeSelect,
12
- Cascader,
13
- DatePicker,
14
- TimePicker,
15
- Radio,
16
- Checkbox,
17
- Switch,
18
- Form,
19
- } from 'n-designv3';
20
-
21
- const COMPONENTS = {
22
- Input,
23
- InputNumber,
24
- Textarea,
25
- Select,
26
- SelectOption: Select.Option,
27
- TreeSelect,
28
- Cascader,
29
- DatePicker,
30
- TimePicker,
31
- Radio,
32
- Checkbox,
33
- RadioGroup: Radio.Group,
34
- CheckboxGroup: Checkbox.Group,
35
- Switch,
36
- Form,
37
- FormItem: Form.Item,
38
- };
39
-
40
- export const NkReadonlyPlugin: Plugin = {
41
- install(app: App) {
42
- for (const [name, Comp] of Object.entries(COMPONENTS)) {
43
- if (Comp) {
44
- const ReadonlyComp = withReadonly(Comp);
45
- app.component(`Nk${name}`, ReadonlyComp);
46
- }
47
- }
48
- },
49
- };
50
-
51
- // 可选:导出 HOC 供用户按需使用
52
- export { withReadonly };
@@ -1,11 +0,0 @@
1
- // src/types/env.d.ts
2
- interface ImportMetaEnv {
3
- readonly DEV: boolean;
4
- readonly PROD: boolean;
5
- readonly SSR: boolean;
6
- // 可以根据实际需要添加其他环境变量
7
- }
8
-
9
- interface ImportMeta {
10
- readonly env: ImportMetaEnv;
11
- }
@@ -1,414 +0,0 @@
1
- // composables/withReadonly.ts
2
- import { defineComponent, h, computed, inject, provide, ref, watch, useAttrs, type PropType, type Component, type VNode, Ref } from 'vue';
3
-
4
- type FormModelValue = string | number | boolean | any[] | null | undefined;
5
- type ValueToLabelFn = (value: FormModelValue) => string;
6
-
7
- // ========================
8
- // 工具函数:提取静态文本(用于单个 checkbox/radio/select )
9
- // ========================
10
- function extractStaticText(vnode: any): string {
11
- if (vnode == null) return '';
12
- if (typeof vnode === 'string') return vnode;
13
- if (Array.isArray(vnode)) return vnode.map(extractStaticText).join('');
14
- if (typeof vnode === 'object') {
15
- if (typeof vnode.children === 'string') {
16
- return vnode.children;
17
- }
18
- // 处理动态插槽内容
19
- if (vnode?.default && typeof vnode.default === 'function') {
20
- const dynamicContent = vnode.default();
21
- if (Array.isArray(dynamicContent)) {
22
- return extractStaticText(dynamicContent);
23
- }
24
- }
25
- if (vnode.children) {
26
- extractStaticText(vnode.children);
27
- }
28
- }
29
- return '';
30
- }
31
-
32
- // ========================
33
- // 从插槽中提取 value → label 映射(支持 n-option / n-radio / n-checkbox / n-select)
34
- // ========================
35
- function collectOptionLabelMapFromSlots(vnodes: VNode | VNode[] | undefined, fieldNames: Record<string, string> = {}): Map<any, string> {
36
- const labelMap = new Map<any, string>();
37
- const { value: valKey = 'value', label: labelKey = 'label' } = fieldNames;
38
-
39
- if (!vnodes) return labelMap;
40
-
41
- const nodes = Array.isArray(vnodes) ? vnodes : [vnodes];
42
-
43
- for (const vnode of nodes) {
44
- if (!vnode || typeof vnode !== 'object') continue;
45
-
46
- // 处理组件节点
47
- if (vnode.type && (typeof vnode.type === 'object' || typeof vnode.type === 'function')) {
48
- let compName = (vnode.type as any).name?.toLowerCase() || '';
49
- if (typeof vnode.type === 'function') {
50
- compName = (vnode.type as any).displayName?.toLowerCase() || '';
51
- } else if (typeof vnode.type === 'string') {
52
- compName = (vnode.type as any)?.toLowerCase() || '';
53
- }
54
- if (/option|radio|checkbox/i.test(compName)) {
55
- const props = vnode.props || {};
56
- const value = props[valKey] ?? props.value;
57
- const labelText = props[labelKey] ?? props.label;
58
- if (value != null) {
59
- const label = extractStaticText(vnode.children) || labelText || String(value);
60
- labelMap.set(value, label);
61
- }
62
- }
63
- }
64
-
65
- // 递归子节点
66
- if (vnode.children) {
67
- const childMap = collectOptionLabelMapFromSlots(vnode.children as any, fieldNames);
68
- childMap.forEach((label, val) => {
69
- if (!labelMap.has(val)) labelMap.set(val, label);
70
- });
71
- }
72
- }
73
-
74
- return labelMap;
75
- }
76
-
77
- // ========================
78
- // Cascader: 递归查找 label 路径
79
- // ========================
80
- function findCascaderLabels(options: any[], value: any[], fieldNames: Record<string, string>): string[] {
81
- const { value: valKey = 'value', label: labelKey = 'label', children: childrenKey = 'children' } = fieldNames;
82
- const labels: string[] = [];
83
- let current = options;
84
-
85
- for (const v of value) {
86
- const node = current.find((item: any) => item[valKey] === v);
87
- if (node) {
88
- labels.push(node[labelKey] || node[valKey] || String(v));
89
- current = node[childrenKey] || [];
90
- } else {
91
- labels.push(String(v));
92
- break;
93
- }
94
- }
95
- return labels;
96
- }
97
-
98
- // ========================
99
- // TreeSelect: 从 treeData 递归查找 label
100
- // ========================
101
- function findTreeSelectLabels(treeData: any[], value: any, fieldNames: Record<string, string>): string[] {
102
- const { value: valKey = 'value', label: labelKey = 'title', children: childrenKey = 'children' } = fieldNames;
103
- const labels: string[] = [];
104
- const targetValues = new Set(Array.isArray(value) ? value : [value]);
105
-
106
- function dfs(nodes: any[]) {
107
- for (const node of nodes) {
108
- if (targetValues.has(node[valKey])) {
109
- labels.push(node[labelKey] || node[valKey]);
110
- }
111
- if (node[childrenKey]) {
112
- dfs(node[childrenKey]);
113
- }
114
- }
115
- }
116
-
117
- dfs(treeData);
118
- return labels;
119
- }
120
-
121
- // ========================
122
- // 获取只读显示文本
123
- // ========================
124
- function getDisplayText({
125
- modelValue,
126
- options,
127
- treeData,
128
- valueToLabel,
129
- fieldNames,
130
- slots,
131
- isSelect,
132
- isRadioGroup,
133
- isCheckboxGroup,
134
- isCheckbox,
135
- isRadio,
136
- isCascader,
137
- isTreeSelect,
138
- isSwitch,
139
- attrs,
140
- emptyText,
141
- }: {
142
- modelValue: FormModelValue;
143
- options?: any[];
144
- treeData?: any[];
145
- valueToLabel?: ValueToLabelFn;
146
- fieldNames: Record<string, string>;
147
- slots?: any;
148
- isSelect: boolean;
149
- isRadioGroup: boolean;
150
- isCheckboxGroup: boolean;
151
- isCheckbox: boolean;
152
- isRadio: boolean;
153
- isCascader: boolean;
154
- isTreeSelect: boolean;
155
- isSwitch: boolean;
156
- attrs: Record<string, any>;
157
- emptyText: string;
158
- }): string {
159
- // 1. 自定义映射
160
- if (valueToLabel) {
161
- try {
162
- return valueToLabel(modelValue) || emptyText;
163
- } catch (e) {
164
- console.warn('[ReadonlyHOC] valueToLabel error', e);
165
- }
166
- }
167
-
168
- // 2. Switch 特殊处理
169
- if (isSwitch) {
170
- const checkedVal = attrs.checkedValue ?? attrs['checked-value'] ?? true;
171
- const uncheckedVal = attrs.uncheckedValue ?? attrs['un-checked-value'] ?? false;
172
- if (modelValue === checkedVal) return attrs.checkedChildren ?? attrs['checked-children'] ?? '开启';
173
- if (modelValue === uncheckedVal) return attrs.uncheckedChildren ?? attrs['un-checked-children'] ?? '关闭';
174
- return String(modelValue || emptyText);
175
- }
176
-
177
- // 3. Cascader
178
- if (isCascader && Array.isArray(modelValue) && options?.length) {
179
- return findCascaderLabels(options, modelValue, fieldNames).join(' / ') || emptyText;
180
- }
181
-
182
- // 4. TreeSelect
183
- if (isTreeSelect && treeData?.length) {
184
- return findTreeSelectLabels(treeData, modelValue, fieldNames).join(', ') || emptyText;
185
- }
186
-
187
- // 5. 动态 options(Select / RadioGroup / CheckboxGroup)
188
- if (options?.length) {
189
- const valKey = fieldNames.value || 'value';
190
- const labelKey = fieldNames.label || 'label';
191
- const getLabel = (val: any) => {
192
- const opt = options.find(item => item[valKey] === val);
193
- return opt ? opt[labelKey] || opt[valKey] : val === undefined || val === null ? '' : String(val);
194
- };
195
- if (Array.isArray(modelValue)) {
196
- return modelValue.map(getLabel).join(', ') || emptyText;
197
- } else {
198
- return getLabel(modelValue) || emptyText;
199
- }
200
- }
201
-
202
- // 6. 静态插槽 fallback(Select / RadioGroup / CheckboxGroup)
203
- else if ((isSelect || isRadioGroup || isCheckboxGroup) && slots?.default) {
204
- try {
205
- const slotContent = slots.default();
206
-
207
- const labelMap = collectOptionLabelMapFromSlots(slotContent, fieldNames);
208
- if (labelMap.size > 0) {
209
- const getLabel = (val: any) => (val === undefined || val === null ? '' : labelMap.get(val) || String(val));
210
- if (Array.isArray(modelValue)) {
211
- return modelValue.map(getLabel).join(', ') || emptyText;
212
- } else {
213
- return getLabel(modelValue) || emptyText;
214
- }
215
- }
216
- } catch (e) {
217
- console.warn('[ReadonlyHOC] Failed to parse slot options', e);
218
- }
219
- }
220
-
221
- // 7. 单个 Checkbox / Radio 静态文本兜底
222
- else if (slots?.default && (isCheckbox || isRadio) && typeof modelValue === 'boolean') {
223
- try {
224
- const content = slots.default();
225
- const text = extractStaticText(content);
226
- if (text) return text;
227
- } catch (e) {
228
- /* ignore */
229
- }
230
- }
231
-
232
- // 8. 最终兜底
233
- if (modelValue == null || modelValue === '') return emptyText;
234
- return Array.isArray(modelValue) ? modelValue.join(', ') : String(modelValue);
235
- }
236
-
237
- // ========================
238
- // 高阶组件
239
- // ========================
240
- export function withReadonly(BaseComponent: Component) {
241
- const baseName = BaseComponent.name || '';
242
- const componentName = baseName.toLowerCase();
243
-
244
- const isSelect = /select/i.test(componentName) && !/tree|cascader/i.test(componentName);
245
- const isRadio = /radio/i.test(componentName) && !/group/i.test(componentName);
246
- const isRadioGroup = /radio.*group/i.test(componentName);
247
- const isCheckbox = /checkbox/i.test(componentName) && !/group/i.test(componentName);
248
- const isCheckboxGroup = /checkbox.*group/i.test(componentName);
249
- const isSwitch = /switch/i.test(componentName);
250
- const isCascader = /cascader/i.test(componentName);
251
- const isTreeSelect = /tree.*select/i.test(componentName);
252
-
253
- const isCheckType = isCheckbox || isSwitch || isRadio;
254
- const nativeProp = isCheckType ? 'checked' : 'value';
255
- const updateEvent = `update:${nativeProp}`;
256
-
257
- const isForm = /form$/i.test(componentName);
258
- const isFormItem = /form.*item/i.test(componentName);
259
-
260
- return defineComponent({
261
- name: baseName,
262
- inheritAttrs: false,
263
- props: {
264
- modelValue: { type: [String, Number, Boolean, Array, Object] as PropType<FormModelValue>, default: undefined },
265
- [nativeProp]: { type: [String, Number, Boolean, Array, Object] as PropType<FormModelValue>, default: undefined },
266
- readonly: { type: Boolean, default: undefined },
267
- emptyText: { type: String, default: '--' },
268
- valueToLabel: { type: Function as PropType<ValueToLabelFn>, default: null },
269
- },
270
- emits: ['update:modelValue', updateEvent],
271
- setup(props, { emit, slots, expose }) {
272
- const globalReadonly = inject<Ref<boolean>>('nkReadonly', ref(false));
273
- const isReadonly = computed(() => props.readonly ?? globalReadonly.value);
274
- const formReadonly = computed(() => props.readonly ?? globalReadonly.value);
275
-
276
- if (isForm) provide('nkReadonly', formReadonly);
277
-
278
- const finalValue = computed(() => (props[nativeProp] !== undefined ? props[nativeProp] : props.modelValue));
279
-
280
- const emitUpdate = (val: FormModelValue) => {
281
- emit('update:modelValue', val);
282
- emit(updateEvent, val);
283
- };
284
-
285
- // ✅ 使用 useAttrs() 保证响应式
286
- const attrs = useAttrs();
287
-
288
- const displayText = computed(() => {
289
- if (!isReadonly.value) return '';
290
-
291
- const fieldNames = attrs.fieldNames ?? attrs['field-names'] ?? {};
292
- const options = attrs.options as any[] | undefined;
293
- const treeData = (attrs.treeData ?? attrs['tree-data']) as any[] | undefined;
294
-
295
- return getDisplayText({
296
- modelValue: finalValue.value as FormModelValue,
297
- options,
298
- treeData,
299
- valueToLabel: props.valueToLabel as ValueToLabelFn,
300
- fieldNames: fieldNames as Record<string, string>,
301
- slots,
302
- isSelect,
303
- isRadioGroup,
304
- isCheckboxGroup,
305
- isCheckbox,
306
- isRadio,
307
- isCascader,
308
- isTreeSelect,
309
- isSwitch,
310
- attrs,
311
- emptyText: props.emptyText as string,
312
- });
313
- });
314
-
315
- // ⚠️ 开发警告
316
- if (import.meta?.env?.DEV) {
317
- watch(
318
- () => isReadonly.value,
319
- readonly => {
320
- if (
321
- readonly &&
322
- !attrs.options &&
323
- !attrs.treeData &&
324
- !props.valueToLabel &&
325
- slots?.default &&
326
- (isSelect || isRadioGroup || isCheckboxGroup || isCascader || isTreeSelect)
327
- ) {
328
- console.warn(
329
- `[ReadonlyHOC] Detected slot-only usage for ${baseName} in readonly mode. ` +
330
- `This works for static content, but will fail for async/dynamic data. ` +
331
- `✅ Recommended: Use \`options\` or \`treeData\` prop instead.`
332
- );
333
- }
334
- },
335
- { immediate: true }
336
- );
337
- }
338
-
339
- const readonlyStyle = {
340
- lineHeight: '32px',
341
- padding: '0 6px',
342
- display: 'inline-block',
343
- minHeight: '32px',
344
- border: 'none',
345
- backgroundColor: 'none',
346
- cursor: 'default',
347
- wordBreak: 'break-all',
348
- color: 'rgba(0, 0, 0, 0.65)',
349
- };
350
-
351
- const formRef = ref<any>();
352
- if (isForm) {
353
- expose({
354
- validate: () => (isReadonly.value ? Promise.resolve({}) : formRef.value?.validate?.()),
355
- validateFields: (names?: string[]) => (isReadonly.value ? Promise.resolve({}) : formRef.value?.validateFields?.(names)),
356
- resetFields: (names?: string[]) => !isReadonly.value && formRef.value?.resetFields?.(names),
357
- clearValidate: (names?: string[]) => !isReadonly.value && formRef.value?.clearValidate?.(names),
358
- scrollToField: (name: string) => !isReadonly.value && formRef.value?.scrollToField?.(name),
359
- });
360
- }
361
-
362
- return () => {
363
- if (isFormItem) {
364
- return h(
365
- BaseComponent,
366
- {
367
- ...attrs,
368
- style: { ...(attrs.style || {}), marginBottom: isReadonly.value ? '10px' : undefined },
369
- },
370
- slots
371
- );
372
- }
373
-
374
- if (isForm) {
375
- return h(
376
- BaseComponent,
377
- {
378
- ...attrs,
379
- rules: formReadonly.value ? null : attrs.rules,
380
- disabled: formReadonly.value ? true : attrs.disabled,
381
- ref: formRef,
382
- },
383
- slots
384
- );
385
- }
386
-
387
- if (isReadonly.value) {
388
- return h(
389
- 'span',
390
- {
391
- class: 'nk-readonly-wrapper',
392
- style: readonlyStyle,
393
- role: 'text',
394
- tabindex: 0,
395
- 'aria-readonly': 'true',
396
- },
397
- displayText.value
398
- );
399
- }
400
-
401
- return h(
402
- BaseComponent,
403
- {
404
- ...attrs,
405
- [nativeProp]: finalValue.value,
406
- [`onUpdate:${nativeProp}`]: emitUpdate,
407
- ref: isForm ? formRef : undefined,
408
- },
409
- slots
410
- );
411
- };
412
- },
413
- });
414
- }
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "useDefineForClassFields": true,
5
- "module": "ESNext",
6
- "lib": ["ES2020", "DOM"],
7
- "moduleResolution": "bundler",
8
- "strict": true,
9
- "jsx": "preserve",
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "declaration": true,
13
- "declarationDir": "./dist",
14
- "outDir": "./dist",
15
- "allowSyntheticDefaultImports": true,
16
- "forceConsistentCasingInFileNames": true
17
- },
18
- "include": ["src"]
19
- }
@@ -1,17 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import vue from '@vitejs/plugin-vue'
3
- import { resolve } from 'path'
4
-
5
- export default defineConfig({
6
- root: 'playground',
7
- plugins: [vue()],
8
- resolve: {
9
- alias: {
10
- '@': resolve(__dirname, 'src'),
11
- },
12
- },
13
- server: {
14
- host: '0.0.0.0',
15
- port: 5200,
16
- },
17
- })
package/vite.config.ts DELETED
@@ -1,20 +0,0 @@
1
- import { resolve } from 'path';
2
- import { defineConfig } from 'vite';
3
- import dts from 'vite-plugin-dts';
4
-
5
- export default defineConfig({
6
- build: {
7
- lib: {
8
- entry: resolve(__dirname, 'src/index.ts'),
9
- name: 'NDesignReadolyPlugin',
10
- fileName: (format) => `index.${format}.js`,
11
- formats: ['es', 'cjs']
12
- },
13
- rollupOptions: {
14
- // external 排除 vue 和 n-designv3
15
- external: ['vue','n-designv3'],
16
- output: { globals: { vue: 'Vue' } }
17
- }
18
- },
19
- plugins: [dts({ insertTypesEntry: true })]
20
- });