소스 검색

添加VbModal组件

Yue 2 년 전
부모
커밋
d72b1b2482
25개의 변경된 파일716개의 추가작업 그리고 182개의 파일을 삭제
  1. 6 0
      components.d.ts
  2. 7 9
      src/assets/sass/_common.scss
  3. 247 0
      src/components/Modals/VbModal.vue
  4. 4 7
      src/components/Table/VbDataTable.vue
  5. 7 1
      src/components/Table/VbTreeTable.vue
  6. 1 1
      src/components/Table/table-partials/TableFooter.vue
  7. 1 1
      src/components/Table/table-partials/table-content/TableContent.vue
  8. 1 1
      src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue
  9. 1 1
      src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue
  10. 1 1
      src/components/Table/table-partials/table-content/table-fixed/TableFixed.vue
  11. 1 1
      src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue
  12. 1 1
      src/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue
  13. 1 1
      src/components/Table/table-partials/table-content/table-footer/TablePagination.vue
  14. 1 1
      src/components/Table/table-partials/table-content/table-head/TableHeadRow.vue
  15. 33 0
      src/components/Tree/OrgSelectTree.vue
  16. 93 0
      src/components/select/AreaCascadeSelect.vue
  17. 1 1
      src/components/select/DateRangeSelect.vue
  18. 1 1
      src/components/select/DySearchSelect.vue
  19. 1 1
      src/components/select/DySelect.vue
  20. 4 9
      src/components/select/DySelectTree.vue
  21. 1 1
      src/components/select/DynamicTreeSelect.vue
  22. 6 2
      src/core/utils/message.ts
  23. 9 0
      src/core/utils/utils.ts
  24. 123 135
      src/views/goLineData/timeData.vue
  25. 164 6
      src/views/overAnalysis/overParam.vue

+ 6 - 0
components.d.ts

@@ -9,6 +9,8 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
+    AreaCascadeSelect: typeof import('./src/components/select/AreaCascadeSelect.vue')['default']
+    AreaSelectCascade: typeof import('./src/components/select/AreaSelectCascade.vue')['default']
     BaseChart: typeof import('./src/components/charts/BaseChart.vue')['default']
     CodeHighlighter: typeof import('./src/components/highlighters/CodeHighlighter.vue')['default']
     CodeHighlighter2: typeof import('./src/components/highlighters/CodeHighlighter2.vue')['default']
@@ -16,11 +18,13 @@ declare module '@vue/runtime-core' {
     DynamicTreeSelect: typeof import('./src/components/select/DynamicTreeSelect.vue')['default']
     DySearchSelect: typeof import('./src/components/select/DySearchSelect.vue')['default']
     DySelect: typeof import('./src/components/select/DySelect.vue')['default']
+    DySelectCascade: typeof import('./src/components/select/DySelectCascade.vue')['default']
     DySelectTree: typeof import('./src/components/select/DySelectTree.vue')['default']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
+    ElElFormItem: typeof import('element-plus/es')['ElElFormItem']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElInput: typeof import('element-plus/es')['ElInput']
@@ -32,6 +36,7 @@ declare module '@vue/runtime-core' {
     Loading: typeof import('./src/components/Table/table-partials/Loading.vue')['default']
     MenuComponent: typeof import('./src/components/menu/MenuComponent.vue')['default']
     OrgCompany: typeof import('./src/components/Tree/OrgCompany.vue')['default']
+    OrgSelectTree: typeof import('./src/components/Tree/OrgSelectTree.vue')['default']
     'PreviewCode ': typeof import('./src/components/prismjs/PreviewCode .vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
@@ -45,6 +50,7 @@ declare module '@vue/runtime-core' {
     TableRightFixed: typeof import('./src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue')['default']
     TableTreeRow: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue')['default']
     VbDataTable: typeof import('./src/components/Table/VbDataTable.vue')['default']
+    VbModal: typeof import('./src/components/Modals/VbModal.vue')['default']
     VbTreeTable: typeof import('./src/components/Table/VbTreeTable.vue')['default']
   }
 }

+ 7 - 9
src/assets/sass/_common.scss

@@ -1,12 +1,3 @@
-// .app-page{
-//   --bs-app-header-base-border-bottom:1px solid #eee;
-
-//   .app-main{
-//     background-color: #f5f5f5;
-//   }
-// }
-
-
 dl{
   margin-bottom: 8px;
   dt{
@@ -16,3 +7,10 @@ dl{
     margin: 0;
   }
 }
+.el-form-item{
+  .el-form-item__label{
+    font-weight: 600;
+    color: #3c3c3c;
+  }
+}
+

+ 247 - 0
src/components/Modals/VbModal.vue

@@ -0,0 +1,247 @@
+<script setup lang="ts">
+import { ref, toRefs, withDefaults, defineExpose, onMounted, watch, nextTick } from "vue"
+import { getAssetPath } from "@/core/helpers/assets"
+import { dialog } from "@/core/utils/message"
+import { Modal } from "bootstrap"
+const props = withDefaults(
+  defineProps<{
+    modal: any //初始化后会把bootstrap的modal对象绑定在外部的modal上,外部请使用 v-model:modal=""
+    id?: string
+    titlePrefix?: string
+    title?: string
+    backdrop?: boolean | "static" //是否有遮罩, 设置"static"时点击遮罩不关闭modal
+    focus?: boolean //初始化时焦点放在modal上
+    keyboard?: boolean //true时按下esc关闭modal
+    showEvent?: (event: Event) => void
+    shownEvent?: (event: Event) => void
+    hideEvent?: (event: Event) => void
+    hiddenEvent?: (event: Event) => void
+    hidePreventedEvent?: (event: Event) => void //backdrop为static:点击遮罩事件或者 keyboard为false时按下esc
+    validateForm?: boolean //验证表单
+    validateFormCallback?: (isValid: boolean, invalidFields?: any) => void //验证表单
+    saveAutoClose?: boolean // 保存时自动关闭modal
+    closeNeedConfrim?: boolean // 关闭模态框需要确认
+    modalFormData?: any // 模态框表单数据
+    formLabelWidth?: string | number // 表单label宽度
+    formSize?: "large" | "default" | "small"
+    closeBtn?: boolean //显示关闭按钮
+    closeBtnText?: string
+    closeBtnClass?: string
+    closeBtnStyle?: string
+    confirmBtn?: boolean //显示确认按钮
+    confirmBtnClass?: string
+    confirmBtnStyle?: string
+    confirmBtnText?: string
+    modalClass?: string
+    modalDialogClass?: string
+    modalContentClass?: string
+    modalHeaderClass?: string
+    modalTitleClass?: string
+    modalBodyClass?: string
+    modalFormClass?: string
+    modalFooterClass?: string
+    modalStyle?: string
+    modalDialogStyle?: string
+    modalContentStyle?: string
+    modalHeaderStyle?: string
+    modalTitleStyle?: string
+    modalBodyStyle?: string
+    modalFormStyle?: string
+    modalFooterStyle?: string
+  }>(),
+  {
+    titlePrefix: "",
+    title: "",
+    id: () => {
+      return `vb_${Math.floor(Math.random() * 999999)}`
+    },
+    backdrop: true,
+    focus: true,
+    keyboard: true,
+    saveAutoClose: true,
+    validateForm: true,
+    formLabelWidth: "100px",
+    formSize: "large",
+    closeNeedConfrim: true,
+    closeBtn: true,
+    closeBtnText: "关闭",
+    closeBtnClass: "btn btn-light",
+    closeBtnStyle: "",
+    confirmBtn: true,
+    confirmBtnText: "确认",
+    confirmBtnClass: "btn btn-primary",
+    confirmBtnStyle: "",
+    modalClass: "",
+    modalDialogClass: "modal-dialog-centered",
+    modalContentClass: "",
+    modalHeaderClass: "",
+    modalTitleClass: "",
+    modalBodyClass: "",
+    modalFormClass: "",
+    modalFooterClass: "",
+    modalStyle: "",
+    modalDialogStyle: "",
+    modalContentStyle: "",
+    modalHeaderStyle: "",
+    modalTitleStyle: "",
+    modalBodyStyle: "",
+    modalFormStyle: "",
+    modalFooterStyle: "",
+  }
+)
+const { modal } = toRefs(props)
+const emits = defineEmits<{
+  (e: "update:modal", modal: Modal): boolean
+  (e: "cancel", form: HTMLFormElement | undefined | null, callback?: (v: boolean) => void): boolean
+  (e: "confirm", form: HTMLFormElement | undefined | null): void
+}>()
+const modalEl = ref<any>()
+const modalFormEl = ref<HTMLFormElement>()
+function show() {
+  modal.value?.show()
+}
+function cancel() {
+  const closeModal = () => {
+    let result = true
+    emits("cancel", modalFormEl.value, (v: boolean) => {
+      result = v
+    })
+    console.log("cancel", result)
+    //callback传false 阻止modal关闭
+    if (result) {
+      nextTick(() => {
+        //modalFormEl.value?.resetFields()
+        modalFormEl.value?.clearValidate()
+      })
+      //modalFormEl.value?.clearValidate()
+      //modalFormEl.value?.resetFields()
+      console.log(" modalFormEl.value", modalFormEl.value)
+      modal.value.hide()
+    }
+  }
+  if (props.closeNeedConfrim) {
+    dialog.confirm("数据还未保存,确认关闭对话框?", "关闭对话框", (confirm: any) => {
+      console.log("confirm", confirm)
+      if (confirm.isConfirmed) {
+        closeModal()
+      }
+    })
+  } else {
+    closeModal()
+  }
+}
+
+function confirm() {
+  if (props.validateForm) {
+    modalFormEl.value?.validate((valid: boolean, fields: any) => {
+      if (props.validateFormCallback) {
+        props.validateFormCallback(valid, fields)
+      } else if (valid) {
+        emits("confirm", modalFormEl.value)
+        if (props.saveAutoClose) {
+          modal.value.hide()
+        }
+      } else {
+        console.error("FORM 验证失败", fields)
+      }
+    })
+  } else {
+    emits("confirm", modalFormEl.value)
+    if (props.saveAutoClose) {
+      modal.value.hide()
+    }
+  }
+}
+
+function init() {
+  if (modalEl.value) {
+    const bsModal = new Modal(modalEl.value, {
+      backdrop: props.backdrop,
+      focus: props.focus,
+      keyboard: props.keyboard,
+    })
+    console.log("+++", bsModal)
+    emits("update:modal", bsModal)
+    modalEl.value.removeEventListener("hide.bs.modal", props.hideEvent, true)
+    modalEl.value.removeEventListener("hidden.bs.modal", props.hiddenEvent, true)
+    modalEl.value.removeEventListener("show.bs.modal", props.showEvent, true)
+    modalEl.value.removeEventListener("shown.bs.modal", props.shownEvent, true)
+    modalEl.value.removeEventListener("hidePrevented.bs.modal", props.hidePreventedEvent, true)
+    if (props.hideEvent) {
+      modalEl.value.addEventListener("hide.bs.modal", props.hideEvent, true)
+    }
+    if (props.hiddenEvent) {
+      modalEl.value.addEventListener("hidden.bs.modal", props.hiddenEvent, true)
+    }
+    if (props.showEvent) {
+      modalEl.value.addEventListener("show.bs.modal", props.showEvent, true)
+    }
+    if (props.shownEvent) {
+      modalEl.value.addEventListener("shown.bs.modal", props.shownEvent, true)
+    }
+    if (props.hidePreventedEvent) {
+      modalEl.value.addEventListener("hidePrevented.bs.modal", props.hidePreventedEvent, true)
+    }
+  }
+}
+watch(modalEl.value, init)
+watch(
+  () => props.id,
+  () => init
+)
+
+onMounted(() => {
+  init()
+})
+defineExpose({ show })
+</script>
+<template>
+  <div ref="modalEl" class="modal modal-lg fade" :class="modalClass" :style="modalStyle" tabindex="-1" :id="id">
+    <div class="modal-dialog" :class="modalDialogClass" :style="modalDialogStyle">
+      <div class="modal-content" :class="modalContentClass" :style="modalContentStyle">
+        <div class="modal-header py-3" :class="modalHeaderClass" :style="modalHeaderStyle">
+          <template v-if="$slots.title">
+            <slot name="title" />
+          </template>
+          <h4 v-else class="modal-title" :class="modalTitleClass" :style="modalTitleStyle">
+            <span class="fs-2 me-5">{{ `${titlePrefix}${title}` }}</span>
+          </h4>
+          <template v-if="$slots.closeBtn">
+            <slot name="closeBtn" />
+          </template>
+          <div v-else class="btn btn-icon btn-sm btn-active-light-primary ms-2" @click="cancel">
+            <span class="svg-icon svg-icon-2x">
+              <inline-svg :src="getAssetPath('media/icons/duotune/arrows/arr011.svg')" />
+            </span>
+          </div>
+        </div>
+        <div class="modal-body py-5" :class="modalBodyClass" :style="modalBodyStyle">
+          <template v-if="$slots.body">
+            <slot name="body" />
+          </template>
+          <el-form
+            v-else
+            ref="modalFormEl"
+            :model="modalFormData"
+            :label-width="`${Number(formLabelWidth) ? formLabelWidth + 'px' : formLabelWidth}`"
+            :size="formSize"
+            :class="modalFormClass"
+            :style="modalFormStyle"
+          >
+            <template v-if="$slots.form">
+              <slot name="form" />
+            </template>
+          </el-form>
+        </div>
+        <div class="modal-footer" :class="modalFooterClass" :style="modalFooterStyle">
+          <button v-if="closeBtn" type="button" :class="closeBtnClass" :style="closeBtnStyle" @click="cancel">
+            {{ closeBtnText }}
+          </button>
+          <button v-if="confirmBtn" type="button" :class="confirmBtnClass" :style="confirmBtnStyle" @click="confirm">
+            {{ confirmBtnText }}
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>

+ 4 - 7
src/components/Table/VbDataTable.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, computed, defineProps, withDefaults, defineEmits, onMounted, watch, defineExpose } from "vue"
+import { ref, toRefs, computed, withDefaults, onMounted, watch, defineExpose } from "vue"
 import TableContent from "@/components/Table/table-partials/table-content/TableContent.vue"
 import TableFooter from "@/components/Table/table-partials/TableFooter.vue"
 import type { Sort, Header, Scroll } from "@/components/Table/table-partials/models"
@@ -93,7 +93,7 @@ const selectedItems = ref(props.selectedItems)
 const remoteTotal = ref<number>(0)
 const remoteData = ref<Array<any>>([])
 const { queryParams: queryParms } = toRefs(props)
-const table = ref()
+const table = ref<any>()
 const dataToDisplay = computed(() => {
   let data = []
   if (props.url) {
@@ -183,10 +183,7 @@ function remote() {
   }
 }
 
-function search(params?: any) {
-  if (params) {
-    queryParms.value.params = params
-  }
+function search() {
   table.value.reset()
   if (props.url) {
     remote()
@@ -232,7 +229,7 @@ watch(
 onMounted(() => {
   search()
 })
-defineExpose({ search, remote })
+defineExpose({ search })
 </script>
 
 <template>

+ 7 - 1
src/components/Table/VbTreeTable.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, withDefaults } from "vue"
 import VbDataTable from "./VbDataTable.vue"
 import type { Sort, Header, Scroll } from "@/components/Table/table-partials/models"
 import type { AxiosRequestConfig } from "axios"
@@ -89,9 +89,15 @@ const onItemSelect = (row: any) => {
 const onItemsPerPageChange = (v: number) => {
   emits("on-items-per-page-change", v)
 }
+const treeTable = ref()
+function search() {
+  treeTable.value.search()
+}
+defineExpose({ search })
 </script>
 <template>
   <VbDataTable
+    ref="treeTable"
     v-model:selected-items="selectedItems"
     :isTree="true"
     :header="header"

+ 1 - 1
src/components/Table/table-partials/TableFooter.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, defineProps, withDefaults, defineEmits, watch, computed, type WritableComputedRef } from "vue"
+import { ref, toRefs, withDefaults, watch, computed, type WritableComputedRef } from "vue"
 import TableItemsPerPageSelect from "@/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue"
 import TablePagination from "./table-content/table-footer/TablePagination.vue"
 

+ 1 - 1
src/components/Table/table-partials/table-content/TableContent.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, computed, watch, defineExpose } from "vue"
+import { ref, withDefaults, computed, watch, defineExpose } from "vue"
 import TableHeadRow from "@/components/Table/table-partials/table-content/table-head/TableHeadRow.vue"
 import TableBodyRow from "@/components/Table/table-partials/table-content/table-body/TableBodyRow.vue"
 import TableFixed from "@/components/Table/table-partials/table-content/table-fixed/TableFixed.vue"

+ 1 - 1
src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, watch, onMounted } from "vue"
+import { ref, withDefaults, watch, onMounted } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
 import TableTreeRow from "./TableTreeRow.vue"
 

+ 1 - 1
src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, onMounted, computed } from "vue"
+import { ref, withDefaults, onMounted, computed } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
 
 const props = withDefaults(

+ 1 - 1
src/components/Table/table-partials/table-content/table-fixed/TableFixed.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, watch, onBeforeUnmount } from "vue"
+import { ref, withDefaults, watch, onBeforeUnmount } from "vue"
 import type { Sort, Header } from "@/components/Table/table-partials/models"
 import TableHeadRow from "@/components/Table/table-partials/table-content/table-head/TableHeadRow.vue"
 import TableBodyRow from "@/components/Table/table-partials/table-content/table-body/TableBodyRow.vue"

+ 1 - 1
src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, watch, onBeforeUnmount } from "vue"
+import { ref, withDefaults, watch, onBeforeUnmount } from "vue"
 import type { Sort, Header } from "@/components/Table/table-partials/models"
 import TableHeadRow from "@/components/Table/table-partials/table-content/table-head/TableHeadRow.vue"
 import TableBodyRow from "@/components/Table/table-partials/table-content/table-body/TableBodyRow.vue"

+ 1 - 1
src/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { toRefs, defineProps, withDefaults, defineEmits, computed, type WritableComputedRef } from "vue"
+import { toRefs, withDefaults, computed, type WritableComputedRef } from "vue"
 
 const props = withDefaults(
   defineProps<{

+ 1 - 1
src/components/Table/table-partials/table-content/table-footer/TablePagination.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { toRefs, defineProps, withDefaults, defineEmits, computed } from "vue"
+import { toRefs, withDefaults, computed } from "vue"
 import { getAssetPath } from "@/core/helpers/assets"
 
 const props = withDefaults(

+ 1 - 1
src/components/Table/table-partials/table-content/table-head/TableHeadRow.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, computed, watch } from "vue"
+import { ref, withDefaults, computed, watch } from "vue"
 import type { Sort, Header } from "@/components/Table/table-partials/models"
 const props = withDefaults(
   defineProps<{

+ 33 - 0
src/components/Tree/OrgSelectTree.vue

@@ -0,0 +1,33 @@
+<script setup lang="ts">
+import { toRefs, withDefaults } from "vue"
+
+const props = withDefaults(
+  defineProps<{
+    value: string | null | undefined
+    class?: string
+  }>(),
+  { class: "full-input" }
+)
+
+const emits = defineEmits<{ (e: "update:value", v: string): void; (e: "select", v: any): void }>()
+const { value } = toRefs(props)
+function onUpdateValue(v: string) {
+  emits("update:value", v)
+}
+function onSelect(v: any) {
+  emits("select", v)
+}
+</script>
+<template>
+  <DySelectTree
+    v-model:value="value"
+    :class="props.class"
+    :static-options="[{ id: '0', label: '根节点' }]"
+    :url="'sys/dict/getOrgList?type=1'"
+    :defaultExpandLevel="1"
+    :option-map="{ id: 'value', label: 'label', children: 'children' }"
+    placeholder="请选择区域..."
+    @update:value="onUpdateValue"
+    @select="onSelect"
+  />
+</template>

+ 93 - 0
src/components/select/AreaCascadeSelect.vue

@@ -0,0 +1,93 @@
+<script setup lang="ts">
+//三级联动
+import { ref, toRefs, withDefaults, onMounted, watch } from "vue"
+import Rs from "@/core/services/RequestService"
+
+const props = withDefaults(defineProps<{ value: Array<string> }>(), { value: () => ["", "", ""] })
+const emits = defineEmits<{
+  (e: "update:value", v: Array<string>): void
+  (e: "change", v: Array<string>): void
+}>()
+const { value: val } = toRefs(props)
+const province = ref(val.value[0] ?? "")
+const city = ref(val.value[1] ?? "")
+const district = ref(val.value[2] ?? "")
+const provinceData = ref<Array<any>>([])
+const cityData = ref<Array<any>>([])
+const districtData = ref<Array<any>>([])
+function provinceChange() {
+  city.value = ""
+  district.value = ""
+  change()
+}
+function cityChange() {
+  district.value = ""
+  change()
+}
+function districtChange() {
+  change()
+}
+function change() {
+  const v = [province.value, city.value, district.value]
+  emits("update:value", v)
+  emits("change", v)
+}
+function init() {
+  Rs.get("sys/dict/getArea").then((res) => {
+    provinceData.value = res.data
+    province.value = val.value[0]
+    city.value = val.value[1]
+    district.value = val.value[2]
+  })
+}
+watch(
+  () => props.value,
+  () => {
+    province.value = val.value[0]
+    cityData.value =
+      provinceData.value.find((vv) => {
+        return vv.value == province.value
+      })?.children ?? []
+    city.value = val.value[1]
+    districtData.value =
+      cityData.value.find((vv) => {
+        return vv.value == city.value
+      })?.children ?? []
+    district.value = val.value[2]
+  }
+)
+onMounted(() => {
+  init()
+})
+</script>
+<template>
+  <el-row :gutter="10">
+    <el-col :span="8">
+      <el-select
+        v-model="province"
+        placeholder="请选择省份"
+        :clearable="true"
+        @change="provinceChange"
+        @clear="provinceChange"
+      >
+        <el-option v-for="(v, i) in provinceData" :key="i" :value="v.value" :label="v.label"></el-option>
+      </el-select>
+    </el-col>
+    <el-col :span="8">
+      <el-select v-model="city" placeholder="请选择城市" :clearable="true" @change="cityChange" @clear="cityChange">
+        <el-option v-for="(v, i) in cityData" :key="i" :value="v.value" :label="v.label"></el-option>
+      </el-select>
+    </el-col>
+    <el-col :span="8">
+      <el-select
+        v-model="district"
+        placeholder="请选择县区"
+        :clearable="true"
+        @change="districtChange"
+        @clear="cityChange"
+      >
+        <el-option v-for="(v, i) in districtData" :key="i" :value="v.value" :label="v.label"></el-option>
+      </el-select>
+    </el-col>
+  </el-row>
+</template>

+ 1 - 1
src/components/select/DateRangeSelect.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, toRefs, withDefaults, onMounted } from "vue"
 import moment from "moment"
 export interface DateSelectOption {
   text: string

+ 1 - 1
src/components/select/DySearchSelect.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, toRefs, withDefaults, onMounted } from "vue"
 import type { AxiosRequestConfig } from "axios"
 import Rs from "@/core/services/RequestService"
 export interface SelectOptionProp {

+ 1 - 1
src/components/select/DySelect.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, toRefs, withDefaults, onMounted } from "vue"
 import type { AxiosRequestConfig } from "axios"
 import Rs from "@/core/services/RequestService"
 export interface SelectOptionProp {

+ 4 - 9
src/components/select/DySelectTree.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, toRefs, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, toRefs, withDefaults, onMounted } from "vue"
 import Treeselect from "vue3-treeselect-ts"
 import "vue3-treeselect-ts/dist/style.css"
 import type { AxiosRequestConfig } from "axios"
@@ -31,7 +31,6 @@ const props = withDefaults(
     }
   }>(),
   {
-    value: "",
     multiple: false,
     clearable: false,
     searchable: false,
@@ -54,8 +53,7 @@ const emits = defineEmits<{
   (e: "update:value", v: string): void
   (e: "select", v: any): void
 }>()
-const { staticOptions, url } = toRefs(props)
-const value = ref(props.value)
+const { staticOptions, url, value } = toRefs(props)
 const options = ref<Array<TreeOption>>(staticOptions.value ?? [])
 
 function formatData(data: any) {
@@ -92,15 +90,12 @@ function init() {
       })
       const val = options.value[0].id
       emits("update:value", val)
-      value.value = val
     })
   }
 }
-
 function onSelect(data: any) {
-  console.log("data", data)
-
-  emits("update:value", data[props.optionMap.id])
+  //console.log("data", data, data.id)
+  emits("update:value", data.id)
   emits("select", data)
 }
 onMounted(() => {

+ 1 - 1
src/components/select/DynamicTreeSelect.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import { ref, withDefaults, onMounted } from "vue"
 import type { AxiosRequestConfig } from "axios"
 import Rs from "@/core/services/RequestService"
 const props = withDefaults(

+ 6 - 2
src/core/utils/message.ts

@@ -7,9 +7,11 @@ const DEFAULT_MESSAGE_BOX_CONFIG = {
     heightAuto: false,
     animation: false,
     customClass: {
-      confirmButton: "btn fw-semobold btn-light-danger",
+      confirmButton: "btn fw-semobold btn-primary",
+      cancelButton: "btn fw-semobold btn-light-danger",
     },
     position: "center",
+    backdrop: `rgba(0,0,123,0.2)`,
   },
   success: {
     icon: "success",
@@ -30,9 +32,11 @@ const DEFAULT_MESSAGE_BOX_CONFIG = {
     showConfirmButton: true,
     title: "您确定吗?",
     //buttons: ['取消', '确定'],
+    cancelButtonText: "取消",
     confirmButtonText: "确定",
+    confirmButtonColor: "#3085d6",
+    cancelButtonColor: "#d33",
     showCancelButton: true,
-    cancelButtonText: "取消",
     focusConfirm: false,
     focusCancel: true,
   },

+ 9 - 0
src/core/utils/utils.ts

@@ -6,6 +6,13 @@ export const isArray = (data: any) => {
   //return data && data.length>0;
   return Array.isArray(data)
 }
+
+export const maper = (target:object,sources:object) => {
+  Object.keys(target).forEach((key) => {
+    target[key]=sources[key]
+  })
+}
+
 export const extend = (...args: Array<any>) => {
   const result = {}
   args.forEach((item: any) => {
@@ -580,7 +587,9 @@ export function debounce(func: any, wait: any, immediate?: boolean) {
 export default {
   has,
   isArray,
+  maper,
   extend,
+  extendDeep,
   getTotal,
   getPercent,
   getPercentByJindu,

+ 123 - 135
src/views/goLineData/timeData.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import { ref, onMounted } from "vue"
 import { getAssetPath } from "@/core/helpers/assets"
-import moment from "moment"
 import type { Header } from "@/components/Table/table-partials/models"
 import Rs from "@/core/services/RequestService"
 import { useRouter } from "vue-router"
@@ -199,6 +198,7 @@ const chartTheme = {
     bottom: 0,
   },
 }
+const modal = ref<any>()
 const detailInfo = ref<any>({})
 const detailDeviceInfo = ref<any>({})
 const chartData1 = ref<any>({})
@@ -231,6 +231,7 @@ const detail = function (row: any) {
       chartData2.value = res.data
     })
   }, 100)
+  modal.value.show()
 }
 
 onMounted(() => {
@@ -298,7 +299,7 @@ onMounted(() => {
           <el-input class="" style="width: 180px" v-model="companyName" placeholder="请输入商户名称" :size="size" />
         </el-form-item>
         <el-form-item class="mb-0 me-5 align-items-center" label="区域">
-          <DySelectTree
+          <!-- <DySelectTree
             v-model:value="orgId"
             style="width: 180px"
             class="full-input"
@@ -306,7 +307,8 @@ onMounted(() => {
             :defaultExpandLevel="1"
             :option-map="{ id: 'value', label: 'label', children: 'children' }"
             placeholder="请选择区域..."
-          />
+          /> -->
+          <OrgSelectTree v-model:value="orgId" style="width: 180px"></OrgSelectTree>
         </el-form-item>
         <el-form-item class="mb-0 me-5 align-items-center" label="菜系">
           <DySelect
@@ -375,142 +377,128 @@ onMounted(() => {
       <div v-if="row['fan_online_state'] == 4">-</div>
     </template>
     <template #action="{ row }">
-      <span class="table-action" data-bs-toggle="modal" data-bs-target="#detail_modal" @click="detail(row)">
-        查看详情
-      </span>
+      <span class="table-action" @click="detail(row)">查看详情</span>
     </template>
   </VbDataTable>
 
-  <div class="modal modal-lg fade" tabindex="-1" id="detail_modal">
-    <div class="modal-dialog">
-      <div class="modal-content">
-        <div class="modal-header py-3">
-          <h5 class="modal-title">
-            <span class="text-info fs-2 me-5">{{ detailInfo?.company_name }}</span>
-            <span class="badge badge-light-primary">{{ detailDeviceInfo.business_state }}</span>
-          </h5>
-          <div class="btn btn-icon btn-sm btn-active-light-primary ms-2" data-bs-dismiss="modal" aria-label="Close">
-            <span class="svg-icon svg-icon-2x">
-              <inline-svg :src="getAssetPath('media/icons/duotune/arrows/arr011.svg')" />
-            </span>
-          </div>
-        </div>
-        <div class="modal-body py-5">
-          <div class="fs-3 fw-bolder mb-3">商户信息</div>
-          <el-row :gutter="20">
-            <el-col :span="12">
-              <dl class="d-flex">
-                <dt class="text">商户名称:</dt>
-                <dd class="text">{{ detailInfo.company_name }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="12">
-              <dl class="d-flex">
-                <dt class="text">负责人:</dt>
-                <dd class="text">{{ detailDeviceInfo.company_contact }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="12">
-              <dl class="d-flex">
-                <dt class="text">详细地址:</dt>
-                <dd class="text">{{ detailDeviceInfo.company_address }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="12">
-              <dl class="d-flex">
-                <dt class="text">联系电话:</dt>
-                <dd class="text">{{ detailDeviceInfo.company_contact_tel }}</dd>
-              </dl>
-            </el-col>
-          </el-row>
-          <div class="separator border-2 my-5"></div>
-          <div class="fs-3 fw-bolder mb-3">设备信息</div>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">净化设施:</dt>
-                <dd class="text">{{ detailDeviceInfo.device_name }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">处理风量:</dt>
-                <dd class="text">{{ detailDeviceInfo.device_air_volume }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">风机状态:</dt>
-                <dd class="text">{{ detailDeviceInfo.fan_online_state }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">风机电流:</dt>
-                <dd class="text">{{ detailDeviceInfo.fan_ia }}</dd>
-              </dl>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">监控仪状态:</dt>
-                <dd class="text">{{ detailDeviceInfo.device_online_state }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">净化器状态:</dt>
-                <dd class="text">{{ detailDeviceInfo.purifier_online_state }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">净化器电流:</dt>
-                <dd class="text">{{ detailDeviceInfo.purifier_ia }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">净化效能:</dt>
-                <dd class="text">{{ detailDeviceInfo.clean_efficiency }}</dd>
-              </dl>
-            </el-col>
-          </el-row>
-          <el-row :gutter="20">
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">油烟浓度:</dt>
-                <dd class="text">{{ detailDeviceInfo.smoke_density }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">非甲烷总烃:</dt>
-                <dd class="text">{{ detailDeviceInfo.pm25 }}</dd>
-              </dl>
-            </el-col>
-            <el-col :span="6">
-              <dl class="d-flex">
-                <dt class="text">颗粒物:</dt>
-                <dd class="text">{{ detailDeviceInfo.voc_density }}</dd>
-              </dl>
-            </el-col>
-          </el-row>
-          <div class="separator border-2 my-5"></div>
-          <div class="d-flex">
-            <div class="w-50">
-              <BaseChart :data="chartData1" :options="chartTheme" type="line" h="280"></BaseChart>
-            </div>
-            <div class="w-50">
-              <BaseChart :data="chartData2" :options="chartTheme" type="line" h="280"></BaseChart>
-            </div>
-          </div>
+  <VbModal v-model:modal="modal" :confirm-btn="false" close-btn-class="btn btn-primary" :close-need-confrim="false">
+    <template #title>
+      <h5 class="modal-title">
+        <span class="text-info fs-2 me-5">{{ detailInfo?.company_name }}</span>
+        <span class="badge badge-light-primary">{{ detailDeviceInfo.business_state }}</span>
+      </h5>
+    </template>
+    <template #body>
+      <div class="fs-3 fw-bolder mb-3">商户信息</div>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <dl class="d-flex">
+            <dt class="text">商户名称:</dt>
+            <dd class="text">{{ detailInfo.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl class="d-flex">
+            <dt class="text">负责人:</dt>
+            <dd class="text">{{ detailDeviceInfo.company_contact }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl class="d-flex">
+            <dt class="text">详细地址:</dt>
+            <dd class="text">{{ detailDeviceInfo.company_address }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl class="d-flex">
+            <dt class="text">联系电话:</dt>
+            <dd class="text">{{ detailDeviceInfo.company_contact_tel }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator border-2 my-5"></div>
+      <div class="fs-3 fw-bolder mb-3">设备信息</div>
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">净化设施:</dt>
+            <dd class="text">{{ detailDeviceInfo.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">处理风量:</dt>
+            <dd class="text">{{ detailDeviceInfo.device_air_volume }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">风机状态:</dt>
+            <dd class="text">{{ detailDeviceInfo.fan_online_state }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">风机电流:</dt>
+            <dd class="text">{{ detailDeviceInfo.fan_ia }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">监控仪状态:</dt>
+            <dd class="text">{{ detailDeviceInfo.device_online_state }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">净化器状态:</dt>
+            <dd class="text">{{ detailDeviceInfo.purifier_online_state }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">净化器电流:</dt>
+            <dd class="text">{{ detailDeviceInfo.purifier_ia }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">净化效能:</dt>
+            <dd class="text">{{ detailDeviceInfo.clean_efficiency }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">油烟浓度:</dt>
+            <dd class="text">{{ detailDeviceInfo.smoke_density }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">非甲烷总烃:</dt>
+            <dd class="text">{{ detailDeviceInfo.pm25 }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="6">
+          <dl class="d-flex">
+            <dt class="text">颗粒物:</dt>
+            <dd class="text">{{ detailDeviceInfo.voc_density }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator border-2 my-5"></div>
+      <div class="d-flex">
+        <div class="w-50">
+          <BaseChart :data="chartData1" :options="chartTheme" type="line" h="280"></BaseChart>
         </div>
-        <div class="modal-footer">
-          <button type="button" class="btn btn-primary" data-bs-dismiss="modal">关闭</button>
+        <div class="w-50">
+          <BaseChart :data="chartData2" :options="chartTheme" type="line" h="280"></BaseChart>
         </div>
       </div>
-    </div>
-  </div>
+    </template>
+  </VbModal>
 </template>

+ 164 - 6
src/views/overAnalysis/overParam.vue

@@ -1,10 +1,14 @@
 <script setup lang="ts">
-import { ref } from "vue"
+import { ref, computed } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
+import { maper } from "@/core/utils/utils"
+import Rs from "@/core/services/RequestService"
+
+const table = ref<any>()
 const cols = ref<Array<Header>>([
   { name: "名称", field: "name", align: "left", width: "200px" },
   { name: "简称", field: "simple_name" },
-  { name: "父级", field: "parent_id" },
+  //{ name: "父级", field: "parent_id" },
   { name: "code", field: "code" },
   { name: "省/市/区", field: "province_city_district" },
   { name: "描述", field: "description" },
@@ -14,16 +18,90 @@ const cols = ref<Array<Header>>([
     width: "150px",
   },
 ])
+const modal = ref<any>()
+//定义需要操作的类型
+const opreationType = ref<"A" | "U" | "D">("A")
+const modalTitlePrefix = computed(() => {
+  return opreationType.value == "A" ? "新增" : opreationType.value == "U" ? "编辑" : ""
+})
+//后期api完善了这里可以改成 返回Promise的function
+const saveUrl = computed(() => {
+  return opreationType.value == "A"
+    ? "sys/organizational/insert"
+    : opreationType.value == "U"
+    ? "sys/organizational/update"
+    : opreationType.value == "D"
+    ? "sys/organizational/delById"
+    : ""
+})
+
+//只放增改传到后台的共用参数,其余需要modal显示的参数另外定义
+const emptyData = {
+  org_id: "",
+  parent_id: "",
+  name: "",
+  simple_name: "",
+  code: "",
+  description: "",
+  province: "",
+  city: "",
+  district: "",
+}
+const formData = ref(emptyData)
+
+const province_city_district = ref(["", "", ""])
 
+function pcdChange(v: Array<string>) {
+  formData.value.province = v[0]
+  formData.value.city = v[1]
+  formData.value.district = v[2]
+}
+function add() {
+  opreationType.value = "A"
+  formData.value = Object.assign({}, emptyData)
+  modal.value.show()
+}
 function edit(row: any) {
-  console.log("ROW", row)
+  //console.log("ROW", row)
+  opreationType.value = "U"
+  province_city_district.value = row.province_city_district
+  formData.value = Object.assign({}, emptyData)
+  maper(formData.value, row)
+  modal.value.show()
+}
 
-  //
+function deleteRow(row: any) {
+  //console.log("DELETE_ROW", row)
+  opreationType.value = "D"
+  Rs.post(saveUrl.value, { data: [row.org_id] }).then(() => {
+    table.value.search()
+  })
+}
+function onCancel(form: any) {
+  console.log("CANCEL", form)
 }
+function onSave(form: any) {
+  console.log("SAVE", form)
+  if (saveUrl.value) {
+    Rs.post(saveUrl.value, { data: formData.value }).then(() => {
+      console.log("TABLE", table.value.search, "==", table.value)
+      table.value.search()
+    })
+  }
+}
+function test(e: Event) {
+  console.log(e)
+}
+/**
+ * 
+
+
+ */
 </script>
 
 <template>
   <VbTreeTable
+    ref="table"
     url="sys/organizational/selectForPage"
     method="post"
     :header="cols"
@@ -35,9 +113,89 @@ function edit(row: any) {
     :fixed-number="1"
     :fixed-right-number="1"
   >
+    <template #table-tool="">
+      <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="add">新增</el-button>
+    </template>
+    <template #province_city_district="{ row }">
+      {{
+        `${row.province_name ?? ""}${row.city_name ? `/${row.city_name}` : ``}${
+          row.district_name ? `/${row.district_name}` : ``
+        }`
+      }}
+    </template>
     <template #action="{ row }">
-      <a class="text-primary table-action" @click="edit(row)">编辑</a>
-      <a class="text-danger table-action" @click="edit(row)">删除</a>
+      <span class="text-primary table-action" @click="edit(row)">编辑</span>
+      <span class="text-danger table-action" @click="deleteRow(row)">删除</span>
     </template>
   </VbTreeTable>
+  <VbModal
+    v-model:modal="modal"
+    :modal-form-data="formData"
+    backdrop="static"
+    :keyboard="false"
+    :title-prefix="modalTitlePrefix"
+    title="组织机构"
+    form-label-width="70"
+    modal-body-class="px-10"
+    @cancel="onCancel"
+    @confirm="onSave"
+    :show-event="test"
+    :shown-event="test"
+    :hidden-event="test"
+    :hide-event="test"
+    :hide-prevented-event="test"
+  >
+    <template #form>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item
+            label="父组织"
+            prop="parent_id"
+            required
+            :rules="[{ required: true, message: '必填', trigger: 'blur' }]"
+          >
+            <OrgSelectTree v-model:value="formData.parent_id"></OrgSelectTree>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item
+            label="CODE"
+            prop="code"
+            required
+            :rules="[
+              { required: true, message: '必填', trigger: 'blur' },
+              { min: 2, max: 4, message: '2至4个字符', trigger: 'blur' },
+            ]"
+          >
+            <el-input v-model="formData.code"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item
+            label="名称"
+            prop="name"
+            required
+            :rules="[{ required: true, message: '必填', trigger: 'blur' }]"
+          >
+            <el-input v-model="formData.name"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="简称" prop="simple_name">
+            <el-input v-model="formData.simple_name"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="省/市/区" prop="province_city_district">
+            <AreaCascadeSelect v-model:value="province_city_district" @change="pcdChange"></AreaCascadeSelect>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="描述" prop="description">
+            <el-input type="textarea" v-model="formData.description"></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
 </template>