浏览代码

完成大部分页面编辑

xiaochan 2 年之前
父节点
当前提交
9913d4ef9d

+ 46 - 15
src/components/query-components/XcPageTable.vue

@@ -1,8 +1,10 @@
 <script setup lang="ts">
-import {defineProps, computed, defineEmits, onMounted} from 'vue'
+import {defineProps, defineEmits, computed, h, onMounted, ref, watch} from 'vue'
 import {componentType} from "./page-help-type";
 import {useVModels} from "@vueuse/core";
-import {ElPagination} from "element-plus";
+import * as el from 'element-plus'
+import {deleteFunction} from "@/components/query-components/page-help";
+import {ElTable} from "element-plus";
 
 const props = defineProps<{
   pageJson: componentType,
@@ -12,29 +14,58 @@ const emits = defineEmits(['update:pageJson'])
 const vModels = useVModels(props, emits)
 
 const onCurrentChange = async (val) => {
-  await vModels.pageJson.value.submitClickFunc();
   vModels.pageJson.value.pageConfig.currentPage = val;
 }
 
 const onSizeChange = async (val) => {
-  await vModels.pageJson.value.submitClickFunc();
   vModels.pageJson.value.pageConfig.pageSize = val;
 }
 
+const tempData = computed(() => {
+  let {currentPage, pageSize} = vModels.pageJson.value.pageConfig
+  return vModels.pageJson.value.tableConfig.data.slice(
+      (currentPage - 1) * pageSize, currentPage * pageSize
+  );
+});
+
+let elComp = ''
+const performRendering = (item, scope) => {
+  let func = new Function('el', `${elComp} \n return ${deleteFunction(item.render)}`);
+  return func(el)(h, scope);
+}
+
+const elPlus = () => {
+  let str = []
+  for (const key in el) {
+    if (key.startsWith("El")) {
+      str.push(` ${key} `)
+    }
+  }
+  elComp = `let {${str.join(',')}} = el`
+}
+
+onMounted(() => {
+  elPlus()
+})
 </script>
 
 <template>
-  <el-table v-bind="vModels.pageJson.value.tableConfig"
-            :data="vModels.pageJson.value.tableConfig.data">
-    <el-table-column v-for="item in vModels.pageJson.value.tableConfig.columns"
-                     v-bind="item">
-      <Component :is="item.render(item)" v-if="item.render"/>
-    </el-table-column>
-  </el-table>
-  <el-pagination v-bind="vModels.pageJson.value.pageConfig"
-                 @current-change="onCurrentChange"
-                 @size-change="onSizeChange"
-  />
+  <div v-loading="vModels.pageJson.value.tableConfig.loading">
+    <el-table v-bind="vModels.pageJson.value.tableConfig"
+              style="width: 100%"
+              highlight-current-row
+              :data="tempData">
+      <el-table-column v-for="item in vModels.pageJson.value.columns"
+                       v-bind="item.bind">
+        <template v-if="item.render" #default="scope">
+          <component :is="performRendering(item,scope)"/>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination v-bind="vModels.pageJson.value.pageConfig"
+                   @current-change="onCurrentChange"
+                   @size-change="onSizeChange"/>
+  </div>
 </template>
 
 <style scoped lang="scss">

+ 74 - 183
src/components/query-components/XcQuery.vue

@@ -1,9 +1,9 @@
 <template>
   <el-form ref=formRef
            :rules="rules"
-           :model="props.testJson.queryParam"
-           :inline="testJson.formConfig.inline || true">
-    <div v-for="(value,key) in testJson.header"
+           :model="props.pageJson.queryParam"
+           :inline="pageJson.formConfig.inline || true">
+    <div v-for="(value,key) in pageJson.header"
          style="display:inline-block;"
          @click="itemClick(value,key)"
          :class="formItemClass(key)">
@@ -27,7 +27,7 @@
       <el-form-item :label="value.label"
                     :prop="value.key">
         <component
-            v-model="testJson.queryParam[value.key]"
+            v-model="pageJson.queryParam[value.key]"
             v-bind="value.bind"
             :is="el[value.name]">
           <template v-if="value.selectData && value.name ==='ElSelect'">
@@ -42,7 +42,7 @@
       </el-form-item>
     </div>
     <el-form-item>
-      <el-button icon="Search" type="primary" @click="submit">查询</el-button>
+      <el-button icon="Search" type="primary" @click="submit(true)">查询</el-button>
       <el-button icon="RefreshLeft" @click="formReset">重置</el-button>
     </el-form-item>
   </el-form>
@@ -55,14 +55,13 @@ import {onMounted, ref, watch} from 'vue'
 import XEUtils from 'xe-utils'
 import {componentType, headerType} from "./page-help-type";
 import {useVModel} from "@vueuse/core";
-import {getFormatDatetime, currentAndAFewDaysAgo, getDateRangeFormatDate} from '../../utils/date'
+import {currentAndAFewDaysAgo, getFormatDatetime} from '../../utils/date'
 import moment from 'moment'
-import axios from "axios";
-import {pageHelpMitt} from "../../components/query-components/page-help";
-import {xcMessage} from "../../utils/xiaochan-element-plus";
+import {executeSQL, extractQueryData, pageHelpMitt} from "./page-help";
+import convertSql from "@/components/query-components/convert-sql";
 
 const props = defineProps<{
-  testJson: componentType,
+  pageJson: componentType,
   currentIndex: number
 }>()
 
@@ -73,197 +72,68 @@ const emits = defineEmits([
 ])
 
 const formRef = ref<FormInstance | null>(null)
-const paramType = new Map<string, any>()
 const rules = ref<FormRules>({})
 
 const queryParam = ref({})
 
-let tempQuerySql = ''
-
-const testJson = useVModel(props, 'testJson', emits)
+const pageJson = useVModel(props, 'pageJson', emits)
 const propsCurrentIndex = useVModel(props, 'currentIndex', emits)
 
-/**
- * @deprecated
- * @param str 字符串
- */
-function replaceTheContent(str) {
-  if (!str) return
-  let one = str[0]
-  let two = str[1]
-  let regex = /{{([\s\S]*?)}}/g;
-  let match = regex.exec(one);
-
-  function clear() {
-    tempQuerySql = tempQuerySql.replace(one, '')
-  }
-
-  if (match) {
-    let key = match[1]
-    let value = testJson.value.queryParam.value[key]
-    if (value != null) {
-      if (XEUtils.isString(value) && XEUtils.isEmpty(value)) {
-        clear()
-      } else {
-        let isString = paramType.get(key) === 'string'
-        let temp = {}
-        if (XEUtils.isArray(value)) {
-          let tempStr = ''
-          let tempLen = value.length
-          value.forEach((item, key) => {
-            tempStr += isString ? "'" + item + "'" : item
-            if (key !== tempLen - 1) {
-              tempStr += ','
-            }
-          })
-          temp[key] = tempStr
-        } else {
-          temp[key] = isString ? "'" + value + "'" : value
-        }
-        tempQuerySql = tempQuerySql.replace(one, XEUtils.template(two, temp))
-      }
-    } else {
-      clear()
-    }
-  }
-}
 
-/**
- * @deprecated
- * @param str 字符串
- */
-function countIfTags(str) {
-  let regex = /<if>([\s\S]*?)<\/if>/g;
-  let match = null
-  do {
-    match = regex.exec(str);
-    if (match) {
-      replaceTheContent(match)
-    }
-  } while (match)
-}
 
-/**
- * 把动态函数 function 前面的 注解删除
- * @param str 返回新的数据
- */
-function deleteFunction(str: string) {
-  // 获取 str 中第一个 function 的下标
-  let index = str.indexOf('function')
-  if (index === -1) {
-    return str;
-  }
-  // 获取 str 中 index 后面的字符串
-  return str.substring(index)
-}
-
-function 提取查询数据() {
-  let queryData = {}
-  testJson.value.header.forEach((item) => {
-    if (isDateRange(item)) {
-      if (testJson.value.queryParam[item.key]) {
-        let {startTime, endTime} = getDateRangeFormatDate(testJson.value.queryParam[item.key])
-        queryData[item.dataRange.startAlias] = startTime
-        queryData[item.dataRange.endAlias] = endTime
-      }
-    } else {
-      if (XEUtils.isArray(testJson.value.queryParam[item.key]) && item.bind.multiple) {
-        let tempVal = ''
-        let valueLen = testJson.value.queryParam[item.key].length
-        // 如果是一个数组的话我就转化成为 xxx,xxx,xxx 这样的字符串
-        testJson.value.queryParam[item.key].forEach((valueItem, key) => {
-          if (item.keyType === 'string') {
-            tempVal += `'${valueItem}'`
-          } else {
-            tempVal += `${valueItem}`
-          }
-          if (key !== valueLen - 1) {
-            tempVal += ','
-          }
-        })
-        if (item.multipleAlias) {
-          queryData[item.multipleAlias] = tempVal
-        } else {
-          queryData[item.key] = tempVal
-        }
-      } else {
-        queryData[item.key] = testJson.value.queryParam[item.key]
-      }
-    }
-  })
-  queryData['pageNo'] = testJson.value.pageConfig.currentPage
-  return queryData
-}
 
-const submit = async () => {
+const submit = async (reset = false) => {
   await formRef.value.validate()
 
-  let queryData;
-  queryData = 提取查询数据();
-
-  let axiosConfig = {
-    method: testJson.value.submitQuery.method,
-    url: testJson.value.submitQuery.url,
+  if (reset) {
+    pageJson.value.pageConfig.currentPage = 1
+    pageJson.value.tableConfig.data = []
+    pageJson.value.pageConfig.total = 0
   }
 
-  if (axiosConfig.method === 'get') {
-    axiosConfig['params'] = queryData
-  } else {
-    axiosConfig['data'] = queryData
-  }
-  let res = null
-  try {
-    res = await axios(axiosConfig)
-  } catch (e) {
-    console.error(e)
-    xcMessage.error(e)
-    throw  new Error(e)
-  }
-  if (res === null) {
-    throw  new Error('res 为空')
-  }
-  if (eval('res.data.' + testJson.value.submitQuery.returnCodeSuccess)) {
-    testJson.value.tableConfig.data = eval('res.data.' + testJson.value.submitQuery.returnsDataPath)
-    testJson.value.pageConfig.total = eval('res.data.' + testJson.value.submitQuery.totalPath)
-  } else {
-    let errorMsg = eval('res.data.' + testJson.value.submitQuery.errorText)
-    xcMessage.error(errorMsg)
-    throw  new Error(errorMsg)
-  }
+  let queryData = extractQueryData(pageJson.value);
 
-  // 动态执行函数,现在暂时不需要
-  // let func = new Function('queryData', 'config', 'axios', 'return ' + deleteFunction(testJson.value.submitEvent))
-  // func()(queryData, testJson.value, axios)
+  let res = await executeSQL(pageJson.value.submitQuerySql, queryData) as any[]
+  pageJson.value.tableConfig.data = res
+  pageJson.value.pageConfig.total = res.length
+
+  // 动态执行函数,暂时先废弃
+  // let func = new Function('queryData', 'config', 'axios', 'return ' + deleteFunction(pageJson.value.submitEvent))
+  // func()(queryData, pageJson.value, axios)
 }
 
 const isDateRange = (item: headerType) => {
   return item.name === 'ElDatePicker' && item.bind.type && item.bind.type === 'daterange'
 }
 
+const isSelectV2 = (item: headerType) => {
+  return item.name === 'ElSelectV2'
+}
+
 const dataReset = () => {
   headerFor(async (item, key) => {
     if (isDateRange(item)) {
       switch (item.dataRange.defaultValue) {
         case '1':
           let temp = getFormatDatetime(new Date, item.bind.valueFormat)
-          testJson.value.queryParam[item.key] = [temp, temp]
+          pageJson.value.queryParam[item.key] = [temp, temp]
           break;
         case '2':
-          testJson.value.queryParam[item.key] = await currentAndAFewDaysAgo(item.dataRange.minusDays, item.bind.valueFormat)
+          pageJson.value.queryParam[item.key] = await currentAndAFewDaysAgo(item.dataRange.minusDays, item.bind.valueFormat)
           break
         case '3':
-          testJson.value.queryParam[item.key] = [moment().startOf('month').format('YYYY-MM-DD'), moment().endOf('month').format('YYYY-MM-DD')]
+          pageJson.value.queryParam[item.key] = [moment().startOf('month').format('YYYY-MM-DD'), moment().endOf('month').format('YYYY-MM-DD')]
           break;
       }
     } else {
-      testJson.value.queryParam[item.key] = item.defaultValue
+      pageJson.value.queryParam[item.key] = item.defaultValue
     }
   })
 }
 
 const headerFor = (iterate: (val: headerType, key: number) => void) => {
-  for (let i = 0; i < testJson.value.header.length; i++) {
-    iterate(testJson.value.header[i], i)
+  for (let i = 0; i < pageJson.value.header.length; i++) {
+    iterate(pageJson.value.header[i], i)
   }
 }
 
@@ -293,7 +163,7 @@ const formItemClass = (index) => {
 
 const upClick = (key) => {
   if (key === 0) return
-  swapItems(testJson.value.header, key, key - 1)
+  swapItems(pageJson.value.header, key, key - 1)
   propsCurrentIndex.value = key - 1
 }
 
@@ -303,41 +173,58 @@ const swapItems = (arr, currentIndex, newIndex) => {
 }
 
 const downClick = (key) => {
-  if (key === testJson.value.header.length - 1) return
-  swapItems(testJson.value.header, key, key + 1)
+  if (key === pageJson.value.header.length - 1) return
+  swapItems(pageJson.value.header, key, key + 1)
   propsCurrentIndex.value = key + 1
 }
 
 const delClick = async (key) => {
   await pageHelpMitt.emit('setShow', false);
-  testJson.value.header.splice(key, 1);
+  pageJson.value.header.splice(key, 1);
   propsCurrentIndex.value = -1;
 }
 
-const initAttribute = () => {
 
-  let data = {
-    pageJson: testJson.value,
-    axios: axios
+const propsFunc = (item: headerType) => {
+  for (const key in item.props) {
+    // 只有搜索是需要特殊处理的
+    // 因为我这是要在后端进行sql查询的
+    if (key === 'remoteMethod') {
+      item.bind[key] = XEUtils.debounce(async (val) => {
+        let queryList = item.props[key].query
+        let prepareData = {}
+        queryList.forEach(queryListItem => {
+          // 如果 queryValue 是空的那么就是自己,不是空的就用 params 里面的 key
+          if (XEUtils.isEmpty(queryListItem.queryValue)) {
+            prepareData[queryListItem.queryName] = val
+          } else {
+            prepareData[queryListItem.queryName] = pageJson.value.queryParam[queryListItem.code]
+          }
+        })
+        item.bind.options = await executeSQL(item.props[key].sql, prepareData)
+      }, 500)
+    } else {
+      item.bind[key] = (val) => {
+        let func = new Function("return " + item.bind[key])
+        func()(item, pageJson.value, val)
+      }
+    }
   }
+}
 
+const initAttribute = () => {
   headerFor((item) => {
-    if (item.bind) {
-      for (const key in item.bind) {
-        if (key.startsWith("_")) {
-          let tempKey = key.substring(1);
-          item.bind[tempKey] = XEUtils.debounce((val) => {
-            let func = new Function('value', 'data', '...val', "return " + item.bind[key])
-            func()(item, data, val)
-          }, 500)
-        }
-      }
+    if (item.props) {
+      propsFunc(item)
+    }
+    if (!item.emits) {
+
     }
   })
 }
 
 
-watch(() => testJson.value.header.length, () => {
+watch(() => pageJson.value.header.length, () => {
   dataReset()
   intiRules()
   initAttribute()
@@ -347,10 +234,14 @@ onMounted(() => {
   dataReset()
   intiRules()
   initAttribute()
-  testJson.value.submitClickFunc = submit
+  pageJson.value.submitClickFunc = submit
   console.log(el)
 })
 
+defineExpose({
+  version: '0.1.1'
+})
+
 </script>
 
 <style lang="scss">

+ 67 - 0
src/components/query-components/convert-sql.ts

@@ -0,0 +1,67 @@
+// 转化Sql 函数 传入  "select * from table  <if test=\"id != null and id != ''\"> id = ${id} </if>" 这样的字符串
+// data 传入 {id: ''} 这样的数据, 如果 id != null && id != '' 那么就生成 select * from table
+// 如果 data 传入 {id: '123'}, 这样的数据那么就生成 select * from table where id = '123'
+function dynamicJudgment(testReg: string, data: any, dataStr: string) {
+    let funcStr = `${dataStr}\nreturn ${testReg}`
+    let func = new Function('data', 'testReg', funcStr)
+    try {
+        return func(data, testReg)
+    } catch (e) {
+        return false
+    }
+}
+
+/**
+ * 把 data 中的 key 变成了
+ * var {id, name} = data
+ * @param data 数据
+ * @returns {string} 返回字符串
+ */
+function getDataParam(data: any): string {
+    let dataParam = ''
+    for (let key in data) {
+        dataParam += `${key},`
+    }
+    dataParam = dataParam.substring(0, dataParam.length - 1)
+    // 这个是把 data 中的 key 给解析出来
+    dataParam = 'var {' + dataParam + '} = ' + 'data'
+    return dataParam
+}
+
+/**
+ * 把 sql 像 mybatis 一样动态拼接
+ * @param  {string} sqlStr sql 字符串
+ * @param {any} data 需要填充的数据
+ * @returns {string} 返回sql
+ */
+const convertSql = (sqlStr: string, data: any): string => {
+    //  去除掉 字符串中的换行,不然  sqlStr.match(reg) 会返回 null
+    sqlStr = sqlStr.replace(/[\r\n]/g, ' ')
+
+    let reg = /<if test="(.+?)">(.+?)<\/if>/g
+    let arr = sqlStr.match(reg)
+    let dataParam = getDataParam(data)
+    if (arr) {
+        arr.forEach(item => {
+            // '<if test="id != null and id != \'\'"> id = ${id} </if>
+            // 获取 test =" 里面的内容
+            let testReg = item.match(/test="(.+?)"/)[1]
+            // 把 and 换成 && 把 or 换成 || 这样 js 才能判断
+            testReg = testReg.replace(/and/g, ' && ').replace(/or/g, ' || ')
+            // 获取到 <if test="id != null and id != \'\'"> id = ${id} </if>
+            // 被 if 包裹的元素
+            if (dynamicJudgment(testReg, data, dataParam)) {
+                let dataStr = item.match(/<if test="(.+?)">(.+?)<\/if>/)[2]
+                sqlStr = sqlStr.replace(item, dataStr)
+            } else {
+                sqlStr = sqlStr.replace(item, '')
+            }
+        })
+    }
+    let 填充数据 = new Function('data', 'sqlStr', `${dataParam} \n return \`${sqlStr}\``)
+    return 填充数据(data, sqlStr)
+}
+
+export default convertSql
+
+

+ 28 - 25
src/components/query-components/page-help-type.ts

@@ -1,6 +1,7 @@
 import {Arrayable} from "element-plus/es/utils";
 import {FormItemRule} from "element-plus/es/tokens/form";
 import {Style} from "util";
+import {StyleValue} from "vue";
 
 export interface codeName {
     value: string | number
@@ -28,6 +29,10 @@ export interface headerType {
     bind?: any | {
         style: CSSStyleDeclaration
     },
+    // 用来重写 props 的函数的
+    props?: any,
+    // 用来重写 emit 的函数的
+    emits?: any
     // 在 el 的组件中文本值
     text?: string
     // el-from 中的 label
@@ -41,14 +46,13 @@ export interface headerType {
     required?: boolean
     rules?: Arrayable<FormItemRule>
     width?: number
-    networkRequests?: networkRequests
     // 初始化就执行的代码
     init?: string
     // 多选别名
     multipleAlias?: string
     // 日期附加信息
     dataRange?: {
-        startAlias: string,
+        startAlias: string
         endAlias: string
         defaultValue: string
         minusDays?: number
@@ -63,40 +67,32 @@ interface formConfig {
 }
 
 export interface componentType {
+    isShow: boolean;
     header: headerType[]
     queryParam: any,
     formConfig: formConfig,
-    // 点击提交时候触发的
+    // 点击提交时候触发的函数
     submitEvent?: string,
-    submitClickFunc?: Function
-    submitQuery: {
-        url: string,
-        method: 'post' | 'get',
-        // 返回体解析
-        returnsDataPath: string;
-        requestInterfaced: '创智中台' | '工作集成平台' | '其他',
-        // 返回成功的信息
-        returnCodeSuccess: string,
-        // 错误提示的消息
-        errorText: string,
-        totalPath: string
-    };
+    // 点击提交的时候触发的sql
+    submitQuerySql: string;
     tableConfig: {
         height?: number,
         data?: any[],
         rowKey?: string,
-        columns?: {
-            prop?: string,
-            label?: string,
-            type?: 'selection' | 'index'
+        loading: boolean,
+    };
+    columns: {
+        bind: {
+            prop: string,
+            label: string,
+            showOverflowTooltip: boolean,
+            type?: 'selection' | 'index' | string
             fixed?: 'left' | 'right' | boolean,
-            showOverflowTooltip?: boolean,
             align?: 'left' | 'center' | 'right',
             width?: number,
-            reserveSelection?: boolean,
-            render?: Function
-        }[],
-    };
+        };
+        render: string
+    }[],
     pageConfig: {
         currentPage: number;
         pageSize: number;
@@ -125,3 +121,10 @@ export interface CompType {
     }
     networkRequests?: networkRequests
 }
+
+interface h {
+    type: string,
+    props: {
+        style: StyleValue
+    },
+}

+ 90 - 41
src/components/query-components/page-help.ts

@@ -1,5 +1,10 @@
-import {elComponentType, headerType} from "./page-help-type";
+import {componentType, elComponentType, headerType} from "./page-help-type";
 import Mitt from "../../utils/mitt";
+import convertSql from './convert-sql'
+import service from "../../api/emr-control/request";
+import XEUtils from "xe-utils";
+import {getDateRangeFormatDate} from '../../utils/date'
+import {AxiosPromise} from "axios";
 
 export const collapseData: {
     title: string,
@@ -71,42 +76,13 @@ export const collapseData: {
                     remote: true,
                     multiple: false,
                     filterable: true,
-                    _remoteMethod: async function (currentValue, data, ...val) {
-                        let {pageJson, axios} = data
-                        currentValue.bind.loading = true
-                        let tempData = {}
-                        let net = currentValue.networkRequests
-
-                        net.params.forEach(item => {
-                            if (item.queryValue) {
-                                tempData[item.queryName] = pageJson.queryParam[item.queryValue]
-                            } else {
-                                tempData[item.queryName] = val[0]
-                            }
-                        })
-
-                        try {
-                            let res = await axios({
-                                method: net.method,
-                                url: net.url,
-                                params: tempData,
-                                data: tempData
-                            })
-                            let a = new Function('res', 'return res.' + net.returnsVolumeResolutionDetail)
-                            currentValue.bind.options = a(res) || []
-                        } catch (e) {
-                            currentValue.bind.options = []
-                        }
-                        currentValue.bind.loading = false
-                    }
                 },
-                networkRequests: {
-                    method: 'get',
-                    url: '/bdp/dataservice/api/4a34eb26909d416188d0cb7d26026b08 ',
-                    params: [{queryName: 'query', queryValue: ''}],
-                    requestInterfaced: '创智中台',
-                    returnsVolumeResolutionDetail: 'data.rows'
-                }
+                props: {
+                    remoteMethod: {
+                        sql: '',
+                        query: [{queryName: 'query', queryValue: ''}],
+                    },
+                },
             }
         ]
     }, {
@@ -265,11 +241,11 @@ export const elComponent: elComponentType = {
     }
 }
 
-export function generateRandomString(length): string {
-    let result = '';
-    let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
-    let charactersLength = characters.length;
-    for (let i = 0; i < length; i++) {
+export function generateRandomString(length: number): string {
+    let result: string = '';
+    let characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+    let charactersLength: number = characters.length;
+    for (let i: number = 0; i < length; i++) {
         result += characters.charAt(Math.floor(Math.random() * charactersLength));
     }
     return result;
@@ -282,3 +258,76 @@ export const interFaced = {
     工作集成平台: 'data.data',
     其他: ''
 }
+
+export const executeSQL = (sqlStr: string, data: any) => {
+    let sql: string = convertSql(sqlStr, data)
+    let res: AxiosPromise<any>
+    try {
+        res = service({
+            url: '/reportCenter/executeSql',
+            method: 'post',
+            data: {sql},
+        })
+        return res
+    } catch (e) {
+        return []
+    }
+}
+
+export const isDateRange = (item: headerType) => {
+    return item.name === 'ElDatePicker' && item.bind.type && item.bind.type === 'daterange'
+}
+
+export function extractQueryData(jsonData: componentType) {
+    let queryData = {}
+    jsonData.header.forEach((item) => {
+        if (isDateRange(item)) {
+            if (jsonData.queryParam[item.key]) {
+                let {startTime, endTime} = getDateRangeFormatDate(jsonData.queryParam[item.key])
+                queryData[item.dataRange.startAlias] = startTime
+                queryData[item.dataRange.endAlias] = endTime
+            } else {
+                queryData[item.dataRange.startAlias] = ''
+                queryData[item.dataRange.endAlias] = ''
+            }
+        } else {
+            if (XEUtils.isArray(jsonData.queryParam[item.key]) && item.bind.multiple) {
+                let tempVal = ''
+                let valueLen = jsonData.queryParam[item.key].length
+                // 如果是一个数组的话我就转化成为 xxx,xxx,xxx 这样的字符串
+                jsonData.queryParam[item.key].forEach((valueItem: string | number, key: number) => {
+                    if (item.keyType === 'string') {
+                        tempVal += `'${valueItem}'`
+                    } else {
+                        tempVal += `${valueItem}`
+                    }
+                    if (key !== valueLen - 1) {
+                        tempVal += ','
+                    }
+                })
+                if (item.multipleAlias) {
+                    queryData[item.multipleAlias] = tempVal
+                } else {
+                    queryData[item.key] = tempVal
+                }
+            } else {
+                queryData[item.key] = jsonData.queryParam[item.key]
+            }
+        }
+    })
+    return queryData
+}
+
+/**
+ * 把动态函数 function 前面的 注解删除
+ * @param str 返回新的数据
+ */
+export function deleteFunction(str: string) {
+    // 获取 str 中第一个 function 的下标
+    let index = str.indexOf('function')
+    if (index === -1) {
+        return str;
+    }
+    // 获取 str 中 index 后面的字符串
+    return str.substring(index)
+}

+ 168 - 0
src/components/query-components/query-warpper.js

@@ -0,0 +1,168 @@
+class QueryWrapper {
+    weList = []
+    // 带 where 条件
+    where = ''
+    // 不带 where
+    notWhere = ''
+
+    constructor() {
+        this.weProxy = new Proxy(this.weList, {
+            set: (target, prop, value) => {
+                if (prop === 'length') {
+                    return true
+                }
+                target[prop] = value
+                if (target.length === 0) {
+                    this.where = ''
+                    this.notWhere = ''
+                } else {
+                    this.where = 'where ' + target.join(' and ')
+                    this.notWhere = target.join(' and ')
+                }
+                return true
+            }
+        })
+    }
+
+    eq(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} = ${val}`);
+        return this;
+    }
+
+    ne(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} <> ${val}`);
+        return this;
+    }
+
+    gt(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} > ${val}`);
+        return this;
+    }
+
+    ge(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} >= ${val}`);
+        return this;
+    }
+
+    lt(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} < ${val}`);
+        return this;
+    }
+
+    le(column, val) {
+        if (typeof val === 'string') {
+            val = `'${val}'`;
+        }
+        this.weProxy.push(`${column} <= ${val}`);
+        return this;
+    }
+
+    like(column, val) {
+        this.weProxy.push(`${column} like '%${val}%'`);
+        return this;
+    }
+
+    in(column, ...val) {
+        if (val != null && val.length > 0) {
+            val = val.map(v => {
+                if (typeof v === 'string') {
+                    return `'${v}'`;
+                }
+                return v;
+            })
+        }
+        this.weProxy.push(`${column} in (${val})`);
+        return this
+    }
+
+    notIn(column, ...val) {
+        if (val != null && val.length > 0) {
+            val = val.map(v => {
+                if (typeof v === 'string') {
+                    return `'${v}'`;
+                }
+                return v;
+            })
+        }
+        this.weProxy.push(`${column} not in (${val})`);
+        return this;
+    }
+
+    isNull(column) {
+        this.weProxy.push(`${column} is null`);
+        return this;
+    }
+
+    isNotNull(column) {
+        this.weProxy.push(`${column} is not null`);
+        return this;
+    }
+
+    /**
+     * 未里面的条件加上一个括号
+     * @param {function(QueryWrapper)} consumer   回调函数
+     * @returns {QueryWrapper} 返回 QueryWrapper
+     */
+    and(consumer) {
+        let tempQw = new QueryWrapper();
+        consumer(tempQw);
+        this.weProxy.push(`(${tempQw.getNotAnd()})`);
+        return this;
+    }
+
+    or() {
+        this.weProxy.push(' or ');
+        return this;
+    }
+
+    get() {
+        return this.weProxy.join(' and ');
+    }
+
+    getNotAnd() {
+        return this.weProxy.join(' ');
+    }
+
+}
+
+
+function test() {
+    const qw = new QueryWrapper();
+    qw.eq('a', '1')
+        .ne("b", 1)
+        .and(temp => {
+            temp.eq('c', 1)
+                .or()
+                .eq('d', 1);
+        })
+        .and(
+            temp => {
+                temp.eq('e', 1)
+                    .or()
+                    .eq('f', 1);
+            }
+        )
+        .eq('startTime', '2012-01-02')
+        .in('a', 1, 2, 23)
+        .notIn('as', 2.2, 23)
+        .isNotNull('c');
+
+    console.log(qw.where, '\n', qw.notWhere)
+}
+
+test()

+ 11 - 10
src/utils/mitt.ts

@@ -1,29 +1,30 @@
 class EventBus {
+
     emits: Map<string, Function> = new Map<string, Function>();
 
-    on(name: string, callback: Function) {
+    on(name: string, callback: Function): void {
         this.emits.set(name, callback);
     };
 
-    off(name: string) {
+    off(name: string): void {
         this.emits.delete(name);
     };
 
-    exists(name: string) {
+    exists(name: string): boolean {
         return this.emits.has(name);
-    }
+    };
 
-    emit(name: string, ...arg: any[]) {
+    emit(name: string, ...arg: any[]): any | null {
         if (this.emits.has(name)) {
-            let cb = this.emits.get(name)
-            let result = null
+            let cb = this.emits.get(name);
+            let result = null;
             try {
-                result = cb!(...arg)
+                result = cb!(...arg);
             } catch {
             }
-            return result
+            return result;
         }
     };
 }
 
-export default EventBus
+export default EventBus;

+ 9 - 0
src/utils/useCompRef.ts

@@ -0,0 +1,9 @@
+import {ref} from "vue";
+
+/**
+ * 获取组件的实例
+ * @param _comp 传入组件
+ */
+export function useCompRef<T extends abstract new(...args: any) => any>(_comp: T) {
+    return ref<InstanceType<T>>();
+}

+ 1 - 1
src/views/hospitalization/zhu-yuan-yi-sheng/cao-yao-yi-zhu/ChaXunChaoYaoYiZhu.vue

@@ -163,7 +163,7 @@ const caoYaoYiZhuXiangQing = (row) => {
 }
 
 const dianJiShanChu = (row, index) => {
-  ElMessageBox.confirm(`您确定要删除<span style="color: red">【${row.orderName}】</span>吗`, '提示', {
+  ElMessageBox.confirm(`您确定要删除<span style="color: red">【${row.orderName}】</span>吗,删除前请先确认做了费用接受重算,这样才会退费。`, '提示', {
     type: 'warning',
     dangerouslyUseHTMLString: true
   }).then(() => {

+ 3 - 125
src/views/settings/Test.vue

@@ -1,132 +1,10 @@
 <template>
-  <el-select-v2 v-model="el" :options="tset"/>
+  <div style="height: 400px;width: 400px">
+
+  </div>
 </template>
 
 <script setup lang="ts">
-import {ref} from 'vue'
 
-const el = ref()
-const tset = [
-  {
-    "code": "1010000",
-    "name": "综合外科",
-    "label": "综合外科",
-    "value": "1010000"
-  },
-  {
-    "code": "1010100",
-    "name": "普外科",
-    "label": "普外科",
-    "value": "1010100"
-  },
-  {
-    "code": "1010200",
-    "name": "泌尿外科",
-    "label": "泌尿外科",
-    "value": "1010200"
-  },
-  {
-    "code": "1010300",
-    "name": "骨科创伤关节",
-    "label": "骨科创伤关节",
-    "value": "1010300"
-  },
-  {
-    "code": "1010400",
-    "name": "神经外科",
-    "label": "神经外科",
-    "value": "1010400"
-  },
-  {
-    "code": "1010500",
-    "name": "心胸外科",
-    "label": "心胸外科",
-    "value": "1010500"
-  },
-  {
-    "code": "1020000",
-    "name": "综合内科",
-    "label": "综合内科",
-    "value": "1020000"
-  },
-  {
-    "code": "1020100",
-    "name": "呼吸内科",
-    "label": "呼吸内科",
-    "value": "1020100"
-  },
-  {
-    "code": "1020200",
-    "name": "消化内科",
-    "label": "消化内科",
-    "value": "1020200"
-  },
-  {
-    "code": "1020300",
-    "name": "神经内科",
-    "label": "神经内科",
-    "value": "1020300"
-  },
-  {
-    "code": "1020400",
-    "name": "心血管内科",
-    "label": "心血管内科",
-    "value": "1020400"
-  },
-  {
-    "code": "1020500",
-    "name": "肾内科",
-    "label": "肾内科",
-    "value": "1020500"
-  },
-  {
-    "code": "1020600",
-    "name": "血液内科",
-    "label": "血液内科",
-    "value": "1020600"
-  },
-  {
-    "code": "1020700",
-    "name": "内分泌与代谢疾病专科",
-    "label": "内分泌与代谢疾病专科",
-    "value": "1020700"
-  },
-  {
-    "code": "1020800",
-    "name": "老年康复科",
-    "label": "老年康复科",
-    "value": "1020800"
-  },
-  {
-    "code": "1030101",
-    "name": "妇科急诊",
-    "label": "妇科急诊",
-    "value": "1030101"
-  },
-  {
-    "code": "1030200",
-    "name": "产科",
-    "label": "产科",
-    "value": "1030200"
-  },
-  {
-    "code": "1030300",
-    "name": "产后康复中心",
-    "label": "产后康复中心",
-    "value": "1030300"
-  },
-  {
-    "code": "1040000",
-    "name": "儿科",
-    "label": "儿科",
-    "value": "1040000"
-  },
-  {
-    "code": "1050100",
-    "name": "眼科",
-    "label": "眼科",
-    "value": "1050100"
-  }
-]
 
 </script>

+ 40 - 39
src/views/utilities/page-editor-help/PageEditorHelp.vue

@@ -1,18 +1,21 @@
 <script setup lang="ts">
-import {nextTick, onMounted, ref} from 'vue'
-import {collapseData, generateRandomString} from "../../../components/query-components/page-help.ts";
+import {onMounted, ref} from 'vue'
+import {collapseData, generateRandomString} from "@/components/query-components/page-help";
 import XcQuery from "../../../components/query-components/XcQuery.vue";
-import {componentType, headerType} from "../../../components/query-components/page-help-type";
-import PageEditotInfo from "./PageEditorInfo.vue";
+import {componentType, headerType} from "@/components/query-components/page-help-type";
+import PageEditotInfo from "./components/PageEditorInfo.vue";
 import {clone} from "xe-utils";
-import PageForm from "../../../views/utilities/page-editor-help/PageForm.vue";
+import PageForm from "./components/PageForm.vue";
 import XcTable from "../../../components/query-components/XcPageTable.vue";
 import * as monaco from 'monaco-editor';
-import {isDev} from "../../../utils/public";
+import {isDev} from "@/utils/public";
+import PageHelpTable from "@/views/utilities/page-editor-help/components/PageHelpTable.vue";
+import sleep from "@/utils/sleep";
+import {useRefHistory} from "@vueuse/core";
 
 const activeNames = ref(['1', '2', '3'])
 const infoRef = ref()
-const tabs = ref('form')
+const tabs = ref('table')
 
 const addEl = (val: headerType) => {
   let key = generateRandomString(5)
@@ -26,26 +29,20 @@ const addEl = (val: headerType) => {
 }
 
 const pageJson = ref<componentType>({
+  isShow: true,
   header: [],
   queryParam: {},
   formConfig: {
     inline: true
   },
-  submitEvent: '/**\n@param {queryData} queryData\n@param {config} config\n@param {axios} axios \n*/\nfunction func(queryData,config,axios){\n console.log(123) \n}',
-  submitQuery: {
-    method: 'get',
-    url: '/bdp/dataservice/api/3d5218ea28d64937ad990f4f8937eb10',
-    returnCodeSuccess: 'result.code === 0',
-    returnsDataPath: 'rows',
-    requestInterfaced: '创智中台',
-    errorText: 'result.message',
-    totalPath: 'pageInfo.recordCounts'
-  },
+  submitEvent: '/**\n@param {queryData} queryData\n@param {config} config\n@param {axios} axios \n*/\nfunction func(queryData,config,axios){\n   \n}',
+  submitQuerySql: '',
   tableConfig: {
-    height: 200,
-    columns: [],
-    data: []
+    height: 600,
+    data: [],
+    loading: false,
   },
+  columns: [],
   pageConfig: {
     currentPage: 1,
     pageSize: 50,
@@ -59,6 +56,9 @@ const pageJson = ref<componentType>({
   }
 })
 
+const {history, undo, redo} = useRefHistory(pageJson, {
+  deep: true,
+})
 
 const theCurrentComponent = ref(-1)
 const itemClick = (data: headerType, index) => {
@@ -99,21 +99,16 @@ const setTsType = () => {
   `)
 }
 
+const formReset = async () => {
+  pageJson.value.isShow = false
+  await sleep(500)
+  pageJson.value.isShow = true
+}
+
 onMounted(() => {
   setTsType()
-  if (isDev) {
-    // addEl(collapseData[1].children[1])
-    addEl(collapseData[2].children[0])
-    pageJson.value.tableConfig.columns = [
-      {type: 'index', label: '排序'},
-      {prop: 'inpatient_no', label: '住院号'},
-      {prop: 'admiss_times', label: '住院次数'},
-      {prop: 'dept_code', label: '科室'}
-    ]
-  }
 })
 
-
 </script>
 
 <template>
@@ -138,12 +133,19 @@ onMounted(() => {
     <el-main>
       <el-auto-resizer>
         <template #default="{ height, width }">
-          <div style="display: flex; overflow-y: auto" :style="{height: height -50 +'px'}">
+          <div style="height: 30px; padding: 5px">
+            <el-button icon="ArrowLeft" @click="undo()">撤销</el-button>
+            <el-button icon="ArrowRight" @click="redo()">重做</el-button>
+            <el-button icon="RefreshLeft" @click="formReset" text>重置</el-button>
+          </div>
+          <div style="display: flex; overflow-y: auto" :style="{height: height -50  - 30 +'px'}">
             <div style="flex: 1; box-sizing: border-box">
-              <xc-query v-model:testJson="pageJson"
-                        v-model:current-index="theCurrentComponent"
-                        @item-click="itemClick"/>
-              <xc-table :page-json="pageJson"/>
+              <template v-if="pageJson.isShow">
+                <xc-query v-model:page-json="pageJson"
+                          v-model:current-index="theCurrentComponent"
+                          @item-click="itemClick"/>
+                <xc-table :page-json="pageJson"/>
+              </template>
             </div>
             <div class="right_box">
               <el-tabs v-model="tabs">
@@ -155,11 +157,10 @@ onMounted(() => {
                 <el-tab-pane label="表单配置" name="form">
                   <page-form :page-json="pageJson"/>
                 </el-tab-pane>
-                <el-tab-pane label="表配置" name="table">
-
+                <el-tab-pane label="表配置" name="table">
+                  <PageHelpTable v-model:page-json="pageJson"/>
                 </el-tab-pane>
               </el-tabs>
-
             </div>
           </div>
         </template>

+ 0 - 79
src/views/utilities/page-editor-help/PageForm.vue

@@ -1,79 +0,0 @@
-<script setup lang="ts">
-import {defineProps, ref} from 'vue'
-import {componentType} from "../../../components/query-components/page-help-type";
-import JsEditDialog from "./JsEditDialog.vue";
-import {ElInput, ElOption, ElSelect} from "element-plus";
-import {interFaced} from "../../../components/query-components/page-help";
-
-const {pageJson} = defineProps<{
-  pageJson: componentType
-}>()
-
-const collapse = ref(['1', '2'])
-
-const dialogConfig = ref({
-  dialog: false
-})
-
-const submit = () => {
-  dialogConfig.value.dialog = true
-}
-
-const changeInterFaced = (val) => {
-  pageJson.submitQuery.returnsVolumeResolutionDetail = interFaced[val]
-}
-
-</script>
-
-<template>
-  <el-form label-width="150px" label-position="left">
-    <el-collapse v-model="collapse">
-      <el-collapse-item title="事件属性" name="1">
-        <el-form-item label="提交事件">
-          <el-button type="info" icon="Edit" round plain @click="submit">编写代码</el-button>
-        </el-form-item>
-      </el-collapse-item>
-
-      <el-collapse-item title="提交请求" name="2">
-
-        <el-form-item label="请求方式">
-          <el-select v-model="pageJson.submitQuery.method">
-            <el-option label="get" value="get"/>
-            <el-option label="post" value="post"/>
-          </el-select>
-        </el-form-item>
-
-        <el-form-item label="请求地址">
-          <el-input type="textarea"
-                    :autosize="{ minRows: 4, maxRows: 10 }"
-                    v-model.trim="pageJson.submitQuery.url"/>
-        </el-form-item>
-
-        <el-form-item label="路径解析">
-          <el-input v-model.trim="pageJson.submitQuery.returnsDataPath"/>
-        </el-form-item>
-
-        <el-form-item label="请求接口">
-          <el-select v-model="pageJson.submitQuery.requestInterfaced"
-                     @change="changeInterFaced">
-            <el-option label="创智中台" value="创智中台"/>
-            <el-option label="工作集成平台" value="工作集成平台"/>
-            <el-option label="其他" value="其他"/>
-          </el-select>
-        </el-form-item>
-
-      </el-collapse-item>
-
-    </el-collapse>
-  </el-form>
-
-  <js-edit-dialog v-if="dialogConfig.dialog"
-                  :page-json="pageJson"
-                  v-model:submit-event="pageJson.submitEvent"
-                  v-model="dialogConfig.dialog"/>
-
-</template>
-
-<style lang="scss">
-
-</style>

+ 14 - 33
src/views/utilities/page-editor-help/JsEditDialog.vue → src/views/utilities/page-editor-help/components/JsEditDialog.vue

@@ -1,14 +1,10 @@
 <script setup lang="ts">
 import {defineProps, nextTick, onMounted, ref} from "vue";
 import {useVModel} from "@vueuse/core";
-import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
-import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
-import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
-import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
-import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
 import * as monaco from 'monaco-editor';
-import sleep from "../../../utils/sleep";
-import {componentType} from "../../../components/query-components/page-help-type";
+import sleep from "@/utils/sleep";
+import {componentType} from "@/components/query-components/page-help-type";
+import {extractQueryData} from "@/components/query-components/page-help";
 
 const props = defineProps<{
   submitEvent: string,
@@ -23,30 +19,16 @@ const submitEventVal = useVModel(props, 'submitEvent', emits)
 const modValue = useVModel(props, 'modelValue', emits)
 
 const jsEdit = ref(null)
-
-// 提示
-self.MonacoEnvironment = {
-  getWorker(_: string, label: string) {
-    if (label === 'json') {
-      return new jsonWorker()
-    }
-    if (label === 'css' || label === 'scss' || label === 'less') {
-      return new cssWorker()
-    }
-    if (label === 'html' || label === 'handlebars' || label === 'razor') {
-      return new htmlWorker()
-    }
-    if (['typescript', 'javascript'].includes(label)) {
-      return new tsWorker()
-    }
-    return new EditorWorker()
-  },
-}
+let monacoEditor
 
 let queryType, configType;
 const setQueryType = () => {
-  let tempStr = `let queryData =  ${JSON.stringify(props.pageJson.queryParam)} `
-  queryType = monaco.languages.typescript.javascriptDefaults.addExtraLib(tempStr, 'queryData');
+  let tempStr = `let queryData =  ${JSON.stringify(extractQueryData(props.pageJson))} `
+  queryType = monaco
+      .languages
+      .typescript
+      .javascriptDefaults
+      .addExtraLib(tempStr, 'queryData');
 }
 
 const setConfigType = () => {
@@ -54,19 +36,20 @@ const setConfigType = () => {
       .languages
       .typescript
       .javascriptDefaults
-      .addExtraLib(`let config = ${JSON.stringify(props.pageJson)}`);
+      .addExtraLib(`let config = ${JSON.stringify(props.pageJson)}`, 'config');
 }
 
 const closed = () => {
   // 销毁类型下一个人要用
   queryType.dispose()
   configType.dispose()
+  monacoEditor.dispose()
   modValue.value = false
 }
 
 onMounted(async () => {
   await nextTick()
-  const monacoEditor = monaco.editor.create(jsEdit.value, {
+  monacoEditor = monaco.editor.create(jsEdit.value, {
     // 初始化的dom节点
     value: submitEventVal.value, // 初始化值
     language: 'javascript', // 语言支持自行查阅demo
@@ -93,13 +76,11 @@ onMounted(async () => {
   setQueryType()
   setConfigType()
 })
-
-
 </script>
 
 <template>
   <el-dialog v-model="dialog" title="代码编辑" draggable @closed="closed">
-    <div ref="jsEdit" style="width: 100%; height: 400px"></div>
+    <div ref="jsEdit" style="width: 100%; height: 400px"/>
   </el-dialog>
 </template>
 

+ 10 - 45
src/views/utilities/page-editor-help/PageEditorInfo.vue → src/views/utilities/page-editor-help/components/PageEditorInfo.vue

@@ -1,12 +1,12 @@
 <script setup lang="ts">
 import {defineExpose, ref, h, nextTick, onMounted} from 'vue'
-import {elComponent, interFaced, pageHelpMitt} from "../../../components/query-components/page-help";
+import {elComponent, interFaced, pageHelpMitt} from "../../../../components/query-components/page-help";
 import {useVModel} from "@vueuse/core";
-import {bindType, componentType, CompType, headerType} from "../../../components/query-components/page-help-type";
+import {bindType, componentType, CompType, headerType} from "../../../../components/query-components/page-help-type";
 import XEUtils from "xe-utils";
 import {ElInput, ElOption, ElSelect, ElSwitch} from 'element-plus'
 import * as el from 'element-plus'
-import XcElOption from "../../../components/xiao-chan/xc-el-option/XcElOption.vue";
+import XcElOption from "../../../../components/xiao-chan/xc-el-option/XcElOption.vue";
 
 const props = defineProps<{
   data: headerType,
@@ -100,7 +100,7 @@ const addSelectData = () => {
 }
 
 const addNetParams = () => {
-  dataVal.value['networkRequests'].params.push({
+  dataVal.value['props'].remoteMethod.query.push({
     queryName: '',
     queryValue: ''
   })
@@ -129,12 +129,6 @@ const test = (value, el) => {
     unboundFunc(value, state)
   }
 }
-
-
-const changeInterFaced = (val) => {
-  dataVal.value.networkRequests.returnsVolumeResolutionDetail = interFaced[val]
-}
-
 const dateDefaultValue = [
   {code: '1', name: '当前'},
   {code: '2', name: '几天前'},
@@ -253,44 +247,18 @@ defineExpose({
               <el-button text type="primary" @click="clearSelectData">清空默认</el-button>
             </div>
           </div>
-
         </template>
 
-        <template v-if="comp.networkRequests">
-          <el-divider content-position="center">
-            远程请求
-          </el-divider>
-
-          <el-form-item label="请求方式">
-            <el-select v-model="dataVal.networkRequests.method">
-              <el-option label="get" value="get"/>
-              <el-option label="post" value="post"/>
-            </el-select>
-          </el-form-item>
-
-          <el-form-item label="请求接口">
-            <el-select v-model="dataVal.networkRequests.requestInterfaced"
-                       @change="changeInterFaced">
-              <el-option label="创智中台" value="创智中台"/>
-              <el-option label="工作集成平台" value="工作集成平台"/>
-              <el-option label="其他" value="其他"/>
-            </el-select>
-          </el-form-item>
+        <template v-if="isSelectV2()">
 
-          <el-form-item label="返回体">
-            <el-input type="textarea"
-                      :autosize="{ minRows: 4, maxRows: 10 }"
-                      v-model.trim="dataVal.networkRequests.returnsVolumeResolutionDetail"/>
-          </el-form-item>
-
-          <el-form-item label="请求地址">
-            <el-input type="textarea"
-                      :autosize="{ minRows: 4, maxRows: 10 }"
-                      v-model.trim="dataVal.networkRequests.url"/>
+          <el-form-item label="执行sql">
+            <el-input v-model="dataVal.props.remoteMethod.sql"
+                      type="textarea"
+                      :autosize="{ minRows: 5, maxRows: 10 }"/>
           </el-form-item>
 
           <el-form-item label="请求参数">
-            <template v-for="item in dataVal.networkRequests.params">
+            <template v-for="item in dataVal.props.remoteMethod.query">
               <el-input v-model="item.queryName" style="width: 100px"/>
               <el-input v-model="item.queryValue" style="width: 100px"/>
             </template>
@@ -299,7 +267,6 @@ defineExpose({
               <el-button text type="primary" @click="addNetParams">新增</el-button>
             </div>
           </el-form-item>
-
         </template>
 
         <template v-if="isDateRange()">
@@ -326,8 +293,6 @@ defineExpose({
           </el-form-item>
 
         </template>
-
-
       </template>
     </el-form>
   </template>

+ 87 - 0
src/views/utilities/page-editor-help/components/PageForm.vue

@@ -0,0 +1,87 @@
+<script setup lang="ts">
+import {defineProps, ref} from 'vue'
+import {componentType} from "@/components/query-components/page-help-type";
+import JsEditDialog from "./JsEditDialog.vue";
+import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
+import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
+import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
+import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
+import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
+import SqlEdit from "@/views/utilities/page-editor-help/components/SqlEdit.vue";
+import {ElTag} from "element-plus";
+
+const {pageJson} = defineProps<{
+  pageJson: componentType
+}>()
+
+// 提示 代码提示
+self.MonacoEnvironment = {
+  getWorker(_: string, label: string) {
+    if (label === 'json') {
+      return new JsonWorker();
+    }
+    if (label === 'css' || label === 'scss' || label === 'less') {
+      return new CssWorker();
+    }
+    if (label === 'html' || label === 'handlebars' || label === 'razor') {
+      return new HtmlWorker();
+    }
+    if (['typescript', 'javascript'].includes(label)) {
+      return new TsWorker();
+    }
+    return new EditorWorker();
+  },
+}
+
+const collapse = ref(['1', '2'])
+const dialogConfig = ref({
+  dialog: false
+})
+
+const sqlEditDialog = ref(false)
+
+const submit = () => {
+  dialogConfig.value.dialog = true
+}
+
+</script>
+
+<template>
+  <el-form label-width="150px" label-position="left">
+    <el-collapse v-model="collapse">
+      <el-collapse-item title="事件属性" name="1">
+        <el-form-item label="提交事件">
+          <el-button type="info" icon="Edit" round plain @click="submit">编写代码</el-button>
+        </el-form-item>
+      </el-collapse-item>
+
+      <el-collapse-item title="运行的sql" name="2">
+        <el-form-item label="sql">
+          <el-button type="info" icon="Edit" round plain @click="sqlEditDialog = true">
+            编辑sql
+          </el-button>
+        </el-form-item>
+      </el-collapse-item>
+    </el-collapse>
+  </el-form>
+
+  <div v-if="pageJson.tableConfig.data.length > 0">
+    <el-tag type="warning" effect="dark">默认展示第一条数据</el-tag>
+    <JsonViewer :value="pageJson.tableConfig.data[0]"
+                style="height: 100%" copyable :expandDepth="3"/>
+  </div>
+
+  <js-edit-dialog v-if="dialogConfig.dialog"
+                  :page-json="pageJson"
+                  v-model:submit-event="pageJson.submitEvent"
+                  v-model="dialogConfig.dialog"/>
+
+  <SqlEdit v-model="sqlEditDialog"
+           v-if="sqlEditDialog"
+           v-model:sql="pageJson.submitQuerySql"/>
+
+</template>
+
+<style lang="scss">
+
+</style>

+ 263 - 0
src/views/utilities/page-editor-help/components/PageHelpColumns.vue

@@ -0,0 +1,263 @@
+<script setup lang="ts">
+import {defineProps, defineEmits, ref, onMounted, watch, computed, nextTick} from 'vue'
+import {componentType} from "@/components/query-components/page-help-type";
+import {useVModels, useWindowSize} from "@vueuse/core";
+import XcElOption from "@/components/xiao-chan/xc-el-option/XcElOption.vue";
+import sleep from "@/utils/sleep";
+import {useCompRef} from "@/utils/useCompRef";
+import {ElTable} from "element-plus";
+import XEUtils from "xe-utils";
+import Sortable from 'sortablejs'
+
+const props = defineProps<{
+  modelValue: boolean
+  pageJson: componentType,
+}>()
+
+const emits = defineEmits(['update:pageJson', 'update:modelValue'])
+const {pageJson, modelValue} = useVModels(props, emits)
+
+const {width, height: winHeight} = useWindowSize();
+
+const dialog = ref(true)
+const tableRef = useCompRef(ElTable)
+const columnsTableRef = useCompRef(ElTable)
+
+const addTableColumn = () => {
+  pageJson.value.columns.push({
+    bind: {
+      prop: '',
+      label: '',
+      showOverflowTooltip: false,
+    },
+    render: ''
+  })
+}
+
+const close = async () => {
+  if (changeColumns) {
+    pageJson.value.isShow = false
+    await sleep(500)
+    pageJson.value.isShow = true
+  }
+  watchColumns()
+}
+
+let watchColumns = null
+let changeColumns = false
+
+const dataTemp = computed(() => {
+  let obj = []
+  if (pageJson.value.tableConfig.data.length > 0) {
+    for (let key in pageJson.value.tableConfig.data[0]) {
+      obj.push({code: key})
+    }
+  }
+  return obj
+})
+
+const columnsKey = computed(() => {
+  let data = []
+  if (pageJson.value.columns.length > 0) {
+    XEUtils.arrayEach(pageJson.value.columns, (item) => {
+      data.push(item.bind.prop)
+    })
+  }
+  return data
+})
+
+const selectedClick = () => {
+  let tempData = <any[]>tableRef.value.getSelectionRows()
+  if (tempData.length === 0) {
+    return
+  }
+  tempData.forEach(item => {
+    if (!columnsKey.value.includes(item.code)) {
+      pageJson.value.columns.push({
+        bind: {
+          prop: item.code,
+          label: item.code,
+          showOverflowTooltip: false,
+        },
+        render: ''
+      })
+    }
+  })
+}
+
+/**
+ * 设置表格列排序
+ */
+const columnsTableSortable = () => {
+  const el = columnsTableRef.value.$el.querySelector('tbody')
+  const ops = {
+    animation: 200, //动画时长
+    handle: '.shangxiatuodong',
+    onEnd({newIndex, oldIndex}) {
+      const currRow = pageJson.value.columns.splice(oldIndex, 1)[0]
+      pageJson.value.columns.splice(newIndex, 0, currRow)
+    }
+  }
+  Sortable.create(el, ops)
+}
+
+const labelInputRef = new Map<number, any>()
+const setLabelInputRef = (el, index) => {
+  labelInputRef.set(index, el)
+}
+
+const findNextInput = (index) => {
+  findInput(index + 1)
+}
+
+const findPrevInput = (index) => {
+  findInput(index - 1)
+}
+
+const findInput = async (index) => {
+  let inputRef = labelInputRef.get(index);
+  if (inputRef) {
+    inputRef.focus();
+    await sleep(200)
+    inputRef.select();
+  }
+}
+
+onMounted(async () => {
+  watchColumns = watch(() => pageJson.value.columns, () => {
+    changeColumns = true
+  }, {deep: true})
+  await nextTick()
+  columnsTableSortable()
+  tableRef.value.toggleAllSelection()
+})
+
+</script>
+
+<template>
+
+  <el-dialog v-model="dialog"
+             title="表格列配置"
+             fullscreen
+             @close="close"
+             @closed="modelValue = false">
+    <div :style="{height:winHeight  - 120 + 'px' }">
+      <el-auto-resizer>
+        <template #default="{ height, width }">
+          <el-table :data="pageJson.columns"
+                    :height="height / 2"
+                    row-key="bind.prop"
+                    ref="columnsTableRef">
+            <el-table-column label="排序" width="50">
+              <template #default>
+                <el-button class="shangxiatuodong"><i class="iconfont icon-shangxiatuodong"></i></el-button>
+              </template>
+            </el-table-column>
+            <el-table-column prop="bind.type" label="类型" width="80">
+              <template #header>
+                <span>类型</span>
+              </template>
+              <template #default="scope">
+                <el-select v-model="scope.row.bind.type" :clearable="true">
+                  <xc-el-option :data="[{code: 'index' , name: '排序'}, {code: 'selected' , name: '多选'}]"/>
+                </el-select>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="bind.prop" label="字段" width="150">
+              <template #default="scope">
+                <el-input v-model.trim="scope.row.bind.prop"/>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="bind.label" label="表头名" width="150">
+              <template #header>
+                <span title="可以按方向键盘来跳转到下一个或上一个">
+                  表头名
+                <el-icon>
+                  <InfoFilled/>
+                </el-icon>
+                </span>
+              </template>
+              <template #default="scope">
+                <el-input v-model.trim="scope.row.bind.label"
+                          @click="labelInputRef.get(scope.$index).select()"
+                          @keydown.up="findPrevInput(scope.$index)"
+                          @keydown.down="findNextInput(scope.$index)"
+                          :ref="(el) => setLabelInputRef(el, scope.$index)"/>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="bind.width" label="宽度" width="70">
+              <template #default="scope">
+                <el-input-number style="width: 60px" :min="0" :controls="false" v-model="scope.row.bind.width"/>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="bind.fixed" label="固定" width="75">
+              <template #default="scope">
+                <el-select v-model="scope.row.bind.fixed" :clearable="true">
+                  <xc-el-option :data="[{code: 'left' , name: '左'}, {code: 'right' , name: '右'}]"/>
+                </el-select>
+              </template>
+            </el-table-column>
+
+            <el-table-column prop="bind.showOverflowTooltip" label="超出宽度显示省略号" width="100">
+              <template #default="scope">
+                <el-switch v-model="scope.row.bind.showOverflowTooltip"
+                           :active-value="true"
+                           :inactive-value="false"/>
+              </template>
+            </el-table-column>
+            <el-table-column prop="render" label="插槽" show-overflow-tooltip/>
+            <el-table-column width="65" fixed="right">
+              <template #header>
+                <el-button @click="addTableColumn" type="primary">添加</el-button>
+              </template>
+              <template #default="scope">
+                <el-button type="danger"
+                           @click="pageJson.columns.splice(scope.$index,1)">
+                  删除
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div :style="{height: height / 2 + 'px'}" class="data_info">
+            <div class="selected_fields">
+              <el-table :data="dataTemp" :height="height / 2" ref="tableRef">
+                <el-table-column type="selection" width="45"/>
+                <el-table-column prop="code" label="字段">
+                  <template #header>
+                    <el-button @click="selectedClick">选中</el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+            <div class="json_show">
+              <div v-if="pageJson.tableConfig.data.length > 0">
+                <el-tag type="warning" effect="dark">默认展示第一条数据</el-tag>
+                <JsonViewer :value="pageJson.tableConfig.data[0]"
+                            style="height: 100%" copyable :expandDepth="3"/>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-auto-resizer>
+    </div>
+  </el-dialog>
+</template>
+
+<style scoped lang="scss">
+.data_info {
+  display: flex;
+
+  .json_show {
+    flex: 1;
+  }
+
+  .selected_fields {
+    width: 400px;
+  }
+
+}
+</style>

+ 252 - 0
src/views/utilities/page-editor-help/components/PageHelpTable.vue

@@ -0,0 +1,252 @@
+<script setup lang="ts">
+import {defineProps, defineEmits, ref, onMounted, nextTick} from 'vue'
+import {componentType} from "@/components/query-components/page-help-type";
+import {useVModels} from "@vueuse/core";
+import * as monaco from "monaco-editor";
+import sleep from "@/utils/sleep";
+import {editor} from "monaco-editor";
+import IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
+import * as el from 'element-plus'
+import {ElTag} from "element-plus";
+import PageHelpColumns from "@/views/utilities/page-editor-help/components/PageHelpColumns.vue";
+
+const props = defineProps<{
+  pageJson: componentType
+}>()
+
+const emits = defineEmits(['update:pageJson'])
+const {pageJson} = useVModels(props, emits)
+let currentIndex = -1
+const editClick = (scope, index) => {
+  currentIndex = index
+  dialog.value = true
+}
+
+const dialog = ref(false)
+const jsonEditRef = ref(null)
+const renderEditRef = ref(null)
+let jsonEditorMonaco: IStandaloneCodeEditor, renderMonaco: IStandaloneCodeEditor
+
+const jsonEdit = async () => {
+  jsonEditorMonaco = monaco.editor.create(jsonEditRef.value, {
+    // 初始化的dom节点
+    value: JSON.stringify(pageJson.value.columns[currentIndex]), // 初始化值
+    language: 'json', // 语言支持自行查阅demo
+    automaticLayout: true, // 自适应布局
+    theme: 'vs-dark', // 官方自带三种主题vs, hc-black, or vs-dark
+    foldingStrategy: 'indentation',
+    renderLineHighlight: 'all', // 行亮
+    selectOnLineNumbers: true, // 显示行号
+    minimap: {
+      enabled: false,
+    },
+    readOnly: false, // 只读
+    fontSize: 16, // 字体大小
+    scrollBeyondLastLine: false, // 取消代码后面一大段空白
+    overviewRulerBorder: true,
+  });
+
+  await sleep(200)
+  jsonEditorMonaco.focus()
+  jsonEditorMonaco.trigger("anything", "editor.action.formatDocument", "")
+}
+
+const renderEdit = async () => {
+  renderMonaco = monaco.editor.create(renderEditRef.value, {
+    // 初始化的dom节点
+    value: pageJson.value.columns[currentIndex].render, // 初始化值
+    language: 'javascript', // 语言支持自行查阅demo
+    automaticLayout: true, // 自适应布局
+    theme: 'vs-dark', // 官方自带三种主题vs, hc-black, or vs-dark
+    foldingStrategy: 'indentation',
+    renderLineHighlight: 'all', // 行亮
+    selectOnLineNumbers: true, // 显示行号
+    minimap: {
+      enabled: false,
+    },
+    readOnly: false, // 只读
+    fontSize: 16, // 字体大小
+    scrollBeyondLastLine: false, // 取消代码后面一大段空白
+    overviewRulerBorder: true,
+  });
+
+  await sleep(200)
+}
+
+const addRender = () => {
+  let str = `/**
+ * @param {h} h 创建 html 标签
+ * @param {scope} scope row表格数据, $index 下标
+ * return 一定要写返回值 返回一个虚拟dom
+ */\nfunction render(h, {row,$index}){\n return \n}`
+  pageJson.value.columns[currentIndex].render = str
+  renderMonaco.setValue(str)
+}
+
+function render(h, {row, $index}) {
+  let {sex, name} = row
+  // 判断如果性别是 1 就显示男性
+  if (sex === 1) {
+    // 这里用的是 element-plus 的 tag标签
+    return h(ElTag, {type: 'success'}, () => '男')
+  } else if (sex === 2) {
+    // 判断如果性别是 2 就显示女性,并且字体标红
+    // 这里用的是浏览器自带的标签需要带上 单引号
+    return h('span', {style: {color: 'red'}}, () => '女')
+  }
+}
+
+const open = async () => {
+  await nextTick()
+  await jsonEdit()
+  await renderEdit()
+  setScopeType()
+}
+
+const closed = () => {
+  pageJson.value.columns[currentIndex] = JSON.parse(jsonEditorMonaco.getValue());
+  pageJson.value.columns[currentIndex].render = renderMonaco.getValue();
+  jsonEditorMonaco.dispose()
+  renderMonaco.dispose()
+}
+
+/**
+ * 用来设置 Render 函数的类型,作用代码提示
+ */
+const setRenderFuncType = () => {
+  let hType = `interface h{
+      type: string | ${elComp},
+      props: {
+        style?: any,
+        class: string
+      },
+      children?: any
+      }`
+
+  monaco
+      .languages
+      .typescript
+      .javascriptDefaults
+      .addExtraLib(hType)
+}
+
+const setScopeType = () => {
+  let columns = []
+  pageJson.value.columns.forEach(item => {
+    if (item.bind.prop) {
+      columns.push(`${item.bind.prop}:string`);
+    }
+  })
+
+  let scopeType = `
+    interface scope{
+      row: {${columns.join(',')}},
+      // 行号
+      $index: number
+    }
+  `
+  monaco
+      .languages
+      .typescript
+      .javascriptDefaults
+      .addExtraLib(scopeType)
+}
+
+let elComp = ''
+const elPlus = () => {
+  let str = []
+  for (const key in el) {
+    if (key.startsWith("El")) {
+      str.push(` ${key} `)
+    }
+  }
+  elComp = `${str.join('|')}`
+}
+
+const addTableColumn = () => {
+  columnsDialog.value = true
+}
+
+const columnsDialog = ref(false)
+
+onMounted(() => {
+  elPlus()
+  setRenderFuncType()
+})
+
+</script>
+
+<template>
+  <PageHelpColumns :page-json="pageJson"
+                   v-model="columnsDialog"
+                   v-if="columnsDialog"/>
+
+  <el-dialog v-model="dialog"
+             fullscreen
+             title="表格属性编辑"
+             @opened="open"
+             @closed="closed">
+    <div style="height: 400px">
+      <div style="display: flex; height: 100%; width: 100%">
+        <div style="height: 100%; width: 30%">
+          <a href="https://element-plus.gitee.io/zh-CN/component/table.html#table-column-%E5%B1%9E%E6%80%A7"
+             target="_blank">element-plus官网</a>
+          <pre>其他bind 属性可以去看
+bind: {
+ prop: 字段名,
+ label: 字段中文名,
+ type?: 'selection'  = 多选 |  'index' = 下标,不支持展开行
+ fixed?: 'left'  = 左固定 | 'right' = 右固定 | boolean,
+ showOverflowTooltip?: boolean,内容过多省略并显示
+ align?: 'left' | 'center' | 'right',
+ width?: number,
+ reserveSelection?: boolean,
+}
+render:用于渲染表格中的数据
+</pre>
+        </div>
+        <div ref="jsonEditRef" style="height: 100%; flex: 1"/>
+      </div>
+    </div>
+
+    <div style="height: 400px">
+      <div style="display: flex; height: 100%; width: 100%">
+        <div style="height: 100%; width: 30%">
+          <el-button @click="addRender" type="primary">添加render</el-button>
+          <pre>
+实例:function render(h,  {row, $index}) {
+  let {sex, name} = scope.row
+  // 判断如果性别是 1 就显示男性
+  if (sex === 1) {
+    // 这里用的是 element-plus 的 tag标签
+    return h(ElTag, {type: 'success'}, () => '男')
+  } else if (sex === 2) {
+    // 判断如果性别是 2 就显示女性,并且字体标红
+    // 这里用的是浏览器自带的标签需要带上 单引号
+    return h('span', {style: {color: 'red'}}, () => '女')
+  }
+}
+          </pre>
+        </div>
+        <div style="height: 100%; flex: 1" ref="renderEditRef"/>
+      </div>
+    </div>
+  </el-dialog>
+
+  <el-button @click="addTableColumn">添加</el-button>
+  <el-table :data="pageJson.columns">
+    <el-table-column prop="bind.prop" label="字段"/>
+    <el-table-column prop="bind.label" label="中文名"/>
+    <el-table-column prop="bind.width" label="宽度"/>
+  </el-table>
+
+
+</template>
+
+<style scoped lang="scss">
+.edit_but {
+  cursor: pointer;
+  color: #2c3e50;
+}
+</style>
+

+ 63 - 0
src/views/utilities/page-editor-help/components/SqlEdit.vue

@@ -0,0 +1,63 @@
+<script setup lang="ts">
+import {defineProps, nextTick, onMounted, ref} from 'vue'
+import {useVModels} from "@vueuse/core";
+import * as monaco from "monaco-editor";
+import sleep from "@/utils/sleep";
+
+const props = defineProps<{
+  modelValue: boolean,
+  sql: string
+}>()
+
+const emits = defineEmits(['update:modelValue', 'update:sql'])
+const propsVal = useVModels(props, emits)
+
+
+const dialog = ref(true)
+const jsEditRef = ref(null)
+
+const close = () => {
+  propsVal.modelValue.value = false
+}
+
+onMounted(async () => {
+  await nextTick()
+  const monacoEditor = monaco.editor.create(jsEditRef.value, {
+    // 初始化的dom节点
+    value: propsVal.sql.value, // 初始化值
+    language: 'sql', // 语言支持自行查阅demo
+    automaticLayout: true, // 自适应布局
+    theme: 'vs-dark', // 官方自带三种主题vs, hc-black, or vs-dark
+    foldingStrategy: 'indentation',
+    renderLineHighlight: 'all', // 行亮
+    selectOnLineNumbers: true, // 显示行号
+    minimap: {
+      enabled: false,
+    },
+    readOnly: false, // 只读
+    fontSize: 16, // 字体大小
+    scrollBeyondLastLine: false, // 取消代码后面一大段空白
+    overviewRulerBorder: true,
+  });
+
+  monacoEditor.onDidChangeModelContent((val) => {
+    propsVal.sql.value = monacoEditor.getValue();
+  });
+
+  await nextTick()
+  await sleep(200)
+
+  monacoEditor.focus()
+})
+
+</script>
+
+<template>
+  <el-dialog v-model="dialog" title="sql编辑" @closed="close" fullscreen>
+    <div ref="jsEditRef" style="width: 100%; height: 800px"/>
+  </el-dialog>
+</template>
+
+<style scoped lang="scss">
+
+</style>