干燥机配套车间生产管理系统/云平台前端
baoshiwei
2023-03-10 1fb197352b6a263646e4ccd3ed1c7854ede031dd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
<template>
  <div v-bind="boxBindProps">
    <!-- 全屏按钮 -->
    <a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen" />
    <textarea ref="textarea" v-bind="getBindValue"></textarea>
  </div>
</template>
 
<script lang="ts">
  import { defineComponent, onMounted, reactive, ref, watch, unref, computed } from 'vue';
  import { propTypes } from '/@/utils/propTypes';
  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
  // 引入全局实例
  import _CodeMirror, { EditorFromTextArea } from 'codemirror';
  // 核心样式
  import 'codemirror/lib/codemirror.css';
  // 引入主题后还需要在 options 中指定主题才会生效
  import 'codemirror/theme/idea.css';
  // 需要引入具体的语法高亮库才会有对应的语法高亮效果
  import 'codemirror/mode/javascript/javascript.js';
  import 'codemirror/mode/css/css.js';
  import 'codemirror/mode/xml/xml.js';
  import 'codemirror/mode/clike/clike.js';
  import 'codemirror/mode/markdown/markdown.js';
  import 'codemirror/mode/python/python.js';
  import 'codemirror/mode/r/r.js';
  import 'codemirror/mode/shell/shell.js';
  import 'codemirror/mode/sql/sql.js';
  import 'codemirror/mode/swift/swift.js';
  import 'codemirror/mode/vue/vue.js';
  // 折叠资源引入:开始
  import 'codemirror/addon/fold/foldgutter.css';
  import 'codemirror/addon/fold/foldcode.js';
  import 'codemirror/addon/fold/brace-fold.js';
  import 'codemirror/addon/fold/comment-fold.js';
  import 'codemirror/addon/fold/indent-fold.js';
  import 'codemirror/addon/fold/foldgutter.js';
  // 折叠资源引入:结束
  //光标行背景高亮,配置里面也需要styleActiveLine设置为true
  import 'codemirror/addon/selection/active-line.js';
  // 支持代码自动补全
  import 'codemirror/addon/hint/show-hint.css';
  import 'codemirror/addon/hint/show-hint.js';
  import 'codemirror/addon/hint/anyword-hint.js';
  import { useAttrs } from '/@/hooks/core/useAttrs';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { isJsonObjectString } from '/@/utils/is.ts';
 
  export default defineComponent({
    name: 'JCodeEditor',
    // 不将 attrs 的属性绑定到 html 标签上
    inheritAttrs: false,
    components: {},
    props: {
      value: propTypes.string.def(''),
      height: propTypes.string.def('auto'),
      disabled: propTypes.bool.def(false),
      // 是否显示全屏按钮
      fullScreen: propTypes.bool.def(false),
      // 全屏以后的z-index
      zIndex: propTypes.any.def(999),
      theme: propTypes.string.def('idea'),
      language: propTypes.string.def(''),
    },
    emits: ['change', 'update:value'],
    setup(props, { emit }) {
      const { prefixCls } = useDesign('code-editer');
      const CodeMirror = window.CodeMirror || _CodeMirror;
      const emitData = ref<object>();
      //表单值
      const [state] = useRuleFormItem(props, 'value', 'change', emitData);
      const textarea = ref<HTMLTextAreaElement>();
      let coder: Nullable<EditorFromTextArea> = null;
      const attrs = useAttrs();
      const height = ref(props.height);
      const options = reactive({
        // 缩进格式
        tabSize: 2,
        // 主题,对应主题库 JS 需要提前引入
        theme: props.theme,
        smartIndent: true, // 是否智能缩进
        // 显示行号
        lineNumbers: true,
        line: true,
        // 启用代码折叠相关功能:开始
        foldGutter: true,
        lineWrapping: true,
        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
        // 启用代码折叠相关功能:结束
        // 光标行高亮
        styleActiveLine: true,
        //代码格式化
        extraKeys: {
          Tab: function autoFormat(editor) {
            //var totalLines = editor.lineCount();
            //editor.autoFormatRange({line:0, ch:0}, {line:totalLines});
            setValue(innerValue, false);
          },
        },
      });
      // 内部存储值,初始为 props.value
      let innerValue = props.value ?? '';
      // 全屏状态
      const isFullScreen = ref(false);
      const fullScreenIcon = computed(() => (isFullScreen.value ? 'fullscreen-exit' : 'fullscreen'));
      // 外部盒子参数
      const boxBindProps = computed(() => {
        let _props = {
          class: [
            prefixCls,
            'full-screen-parent',
            'auto-height',
            {
              'full-screen': isFullScreen.value,
            },
          ],
          style: {},
        };
        if (isFullScreen.value) {
          _props.style['z-index'] = props.zIndex;
        }
        return _props;
      });
      /**
       * 监听组件值
       */
      watch(
        () => props.value,
        () => {
          if (innerValue != props.value) {
            setValue(props.value, false);
          }
        }
      );
      onMounted(() => {
        initialize();
      });
 
      /**
       * 组件赋值
       * @param value
       * @param trigger 是否触发 change 事件
       */
      function setValue(value: string, trigger = true) {
        if (value && isJsonObjectString(value)) {
          value = JSON.stringify(JSON.parse(value), null, 2);
        }
        coder?.setValue(value ?? '');
        innerValue = value;
        trigger && emitChange(innerValue);
      }
 
      //编辑器值修改事件
      function onChange(obj) {
        let value = obj.getValue();
        innerValue = value || '';
        if (props.value != innerValue) {
          emitChange(innerValue);
        }
      }
 
      function emitChange(value) {
        emit('change', value);
        emit('update:value', value);
      }
 
      //组件初始化
      function initialize() {
        coder = CodeMirror.fromTextArea(textarea.value!, options);
        //绑定值修改事件
        coder.on('change', onChange);
        // 初始化成功时赋值一次
        setValue(innerValue, false);
      }
 
      // 切换全屏状态
      function onToggleFullScreen() {
        isFullScreen.value = !isFullScreen.value;
      }
 
      //update-begin-author:taoyan date:2022-5-9 for: codeEditor禁用功能
      watch(
        () => props.disabled,
        (val) => {
          if (coder) {
            coder.setOption('readOnly', val);
          }
        }
      );
      //update-end-author:taoyan date:2022-5-9 for: codeEditor禁用功能
      
      // 支持动态设置语言
      watch(()=>props.language, (val)=>{
        if(val && coder){
          coder.setOption('mode', val);
        }
      });
 
      const getBindValue = Object.assign({}, unref(props), unref(attrs));
 
      //update-begin-author:taoyan date:2022-10-18 for: VUEN-2480【严重bug】online vue3测试的问题 8、online js增强样式问题
      function refresh(){
        if(coder){
          coder.refresh();
        }
      }
      //update-end-author:taoyan date:2022-10-18 for: VUEN-2480【严重bug】online vue3测试的问题 8、online js增强样式问题
      
      return {
        state,
        textarea,
        boxBindProps,
        getBindValue,
        setValue,
        isFullScreen,
        fullScreenIcon,
        onToggleFullScreen,
        refresh
      };
    },
  });
</script>
 
<style lang="less">
  //noinspection LessUnresolvedVariable
  @prefix-cls: ~'@{namespace}-code-editer';
  .@{prefix-cls} {
    &.auto-height {
      .CodeMirror {
        height: v-bind(height) !important;
        min-height: 100px;
      }
    }
 
    /* 全屏样式 */
 
    &.full-screen-parent {
      position: relative;
 
      .full-screen-icon {
        opacity: 0;
        color: black;
        width: 20px;
        height: 20px;
        line-height: 24px;
        background-color: white;
        position: absolute;
        top: 2px;
        right: 2px;
        z-index: 9;
        cursor: pointer;
        transition: opacity 0.3s;
        padding: 2px 0 0 1.5px;
      }
 
      &:hover {
        .full-screen-icon {
          opacity: 1;
 
          &:hover {
            background-color: rgba(255, 255, 255, 0.88);
          }
        }
      }
 
      &.full-screen {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        padding: 8px;
        background-color: #f5f5f5;
 
        .full-screen-icon {
          top: 12px;
          right: 12px;
        }
 
        .full-screen-child,
        .CodeMirror {
          height: 100%;
          max-height: 100%;
          min-height: 100%;
        }
      }
 
      .full-screen-child {
        height: 100%;
      }
    }
    
    /** VUEN-2344【vue3】这个样式有问题,是不是加个边框 */
    .CodeMirror{
      border: 1px solid #ddd;
    }
  }
</style>