Your Name 1 kuukausi sitten
vanhempi
commit
028f577bd5
30 muutettua tiedostoa jossa 5046 lisäystä ja 4 poistoa
  1. 2 2
      admin.ui.plus-master/.env.development
  2. 2 2
      admin.ui.plus-master/.env.production
  3. 23 0
      admin.ui.plus-master/src/api/admin/reportManagement/BlackBox/BlackBoxapi.ts
  4. 55 0
      admin.ui.plus-master/src/api/admin/reportManagement/BlackBox/BlackBoxdto.ts
  5. 127 0
      admin.ui.plus-master/src/views/admin/application/applicationManage/components/org-form.vue
  6. 110 0
      admin.ui.plus-master/src/views/admin/application/applicationManage/components/org-menu.vue
  7. 145 0
      admin.ui.plus-master/src/views/admin/application/applicationManage/index.vue
  8. 246 0
      admin.ui.plus-master/src/views/admin/application/fuelingFTP/index.vue
  9. 264 0
      admin.ui.plus-master/src/views/admin/application/fuelingFTPofDate/index.vue
  10. 14 0
      admin.ui.plus-master/src/views/admin/application/fuelingsdk/README.md
  11. 109 0
      admin.ui.plus-master/src/views/admin/application/fuelingsdk/components/form-audit.vue
  12. 210 0
      admin.ui.plus-master/src/views/admin/application/fuelingsdk/components/form-edit.vue
  13. 346 0
      admin.ui.plus-master/src/views/admin/application/fuelingsdk/index.vue
  14. 115 0
      admin.ui.plus-master/src/views/admin/application/operationLog/index.vue
  15. 243 0
      admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-dot-form.vue
  16. 233 0
      admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-group-form.vue
  17. 276 0
      admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-menu-form.vue
  18. 242 0
      admin.ui.plus-master/src/views/admin/application/permissionManage/index.vue
  19. 110 0
      admin.ui.plus-master/src/views/admin/application/roleManage/components/org-menu.vue
  20. 141 0
      admin.ui.plus-master/src/views/admin/application/roleManage/components/role-form.vue
  21. 152 0
      admin.ui.plus-master/src/views/admin/application/roleManage/components/set-app-menu.vue
  22. 163 0
      admin.ui.plus-master/src/views/admin/application/roleManage/components/set-role-menu.vue
  23. 202 0
      admin.ui.plus-master/src/views/admin/application/roleManage/components/user-select.vue
  24. 373 0
      admin.ui.plus-master/src/views/admin/application/roleManage/index.vue
  25. 202 0
      admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/components/form-edit.vue
  26. 103 0
      admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/components/table-download.vue
  27. 375 0
      admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/index.vue
  28. 160 0
      admin.ui.plus-master/src/views/admin/application/viewManage/components/view-form.vue
  29. 140 0
      admin.ui.plus-master/src/views/admin/application/viewManage/index.vue
  30. 163 0
      admin.ui.plus-master/src/views/admin/statement/BlackBox/index.vue

+ 2 - 2
admin.ui.plus-master/.env.development

@@ -3,8 +3,8 @@ ENV = 'development'
 
 # 本地环境接口地址
 # VITE_API_URL = 'http://ipos.biz:8070'
-# VITE_API_URL = 'http://localhost:8000'
-VITE_API_URL = 'http://dev.hsfuel.com:8070'
+ VITE_API_URL = 'http://localhost:8000'
+#VITE_API_URL = 'http://dev.hsfuel.com:8070'
 # VITE_API_URL = 'http://47.101.220.106:8070'
 # VITE_API_URL = 'http://ipos.biz:8070'
 

+ 2 - 2
admin.ui.plus-master/.env.production

@@ -9,7 +9,7 @@ VITE_PUBLIC_PATH = '/'
 
 # 线上环境接口地址
 # VITE_API_URL = 'http://ipos.biz:8070'
-# VITE_API_URL = 'http://localhost:8000'
-VITE_API_URL = 'http://dev.hsfuel.com:8070'
+ VITE_API_URL = 'http://localhost:8000'
+#VITE_API_URL = 'http://dev.hsfuel.com:8070'
 # VITE_API_URL = 'http://47.101.220.106:8070'
 # VITE_API_URL = 'http://ipos.biz:8070'

+ 23 - 0
admin.ui.plus-master/src/api/admin/reportManagement/BlackBox/BlackBoxapi.ts

@@ -0,0 +1,23 @@
+import { AxiosResponse } from 'axios'
+  import { ContentType, HttpClient, RequestParams } from "/@/api/admin/http-client";
+  import {PageInput,BlackBoxDto} from "/@/api/admin/reportManagement/BlackBox/BlackBoxdto";
+  export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+    /**
+   * No description
+   *
+   * @tags
+   * @name GetList
+   * @summary 查询列表
+   * @request GET:'/api/app/fuel-dispenser/get-black-box-list'
+   * @secure
+   */
+getList = (query:PageInput ,params: RequestParams = {}) : any  =>
+      this.request<BlackBoxDto,any>({
+        path:'/api/app/fuel-dispenser/get-black-box-list',
+        method: 'GET',
+        query:query,
+        secure: true,
+        format: 'json',
+        ...params
+      })
+    }

+ 55 - 0
admin.ui.plus-master/src/api/admin/reportManagement/BlackBox/BlackBoxdto.ts

@@ -0,0 +1,55 @@
+/** 查询信息输出 */
+  export interface ResultOutputPageOutputBlackBoxDto{
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+ data?:PageOutputBlackBoxDto[] | null
+}
+    export interface PageOutputBlackBoxDto{
+      /**
+      * 数据总数
+      * @format int64
+      */
+      total?: number
+      /** 数据 */
+      list?: BlackBoxDto[] | null
+    }
+    export interface BlackBoxDto{
+   /** 石油公司 */
+      oilCompanyName?: string
+   /** 软件版本号 */
+      softwareVersion?: string
+   /** 硬件版本号 */
+      hardwareVersion?: string
+   /** 序列号 */
+      serialNumber?: string
+   /** 绑定加油机厂商 */
+      bindingDispenserManufacturer?: string
+   /** 部件出厂加油机厂商 */
+      componentManufacturer?: string
+   /** 加油站名称 */
+      stationName?: string
+   /** 安全装置厂家 */
+      safetyDeviceManufacturer?: string
+   /** 油机号 */
+      dispenserNumber?: integer
+   /** 油枪号 */
+      nozzleNumber?: integer
+   /** 安全装置状态 */
+      deviceStatus?: string
+   /** 安装时间 */
+      fixTime?: string
+   /** 出厂时间 */
+      soldTime?: string
+
+}
+export interface PageInput {
+      /**当前页数 */
+      currentPage?: number
+      /**展示页数 */
+      pageSize?: number
+    }

+ 127 - 0
admin.ui.plus-master/src/views/admin/application/applicationManage/components/org-form.vue

@@ -0,0 +1,127 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用ID" prop="appid" :rules="[{ required: true, message: '请输入应用ID', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.appid" clearable />
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用名称" prop="name" :rules="[{ required: true, message: '请输入应用名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.name" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用编码" prop="code">
+              <el-input v-model="form.code" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/org/form">
+import { reactive, toRefs, ref, PropType } from 'vue'
+import { OauthApplyDto,OauthApply } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import eventBus from '/@/utils/mitt'
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  orgTreeData: {
+    type: Array as PropType<OauthApply[]>,
+    default: () => [],
+  },
+})
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {
+    id:0,
+    appid: '',
+    name: '',
+    code: '',
+    sort: ''
+  } as OauthApplyDto,
+})
+
+const { form } = toRefs(state)
+
+// 打开对话框
+const open = async (row: any = {}) => {
+  // if (row.id > 0) {
+  //   const res = await new PermissionApi().getApplyInfo({ id: row.id }, { loading: true })
+
+  //   if (res?.success) {
+  //     let formData = res.data as OauthApplyDto
+  //     state.form = formData
+  //   }
+  // } else {
+  //   state.form = {} as OauthApplyDto
+  // }
+  state.form=row
+  state.showDialog = true
+}
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.sureLoading = true
+    let res = {} as any
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addApply(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addApply(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshOrg')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 110 - 0
admin.ui.plus-master/src/views/admin/application/applicationManage/components/org-menu.vue

@@ -0,0 +1,110 @@
+<template>
+  <el-card shadow="never" style="margin-top: 8px" body-style="padding:0px;" class="my-fill">
+    <template #header>
+      <el-input v-model="state.filterText" placeholder="筛选部门" clearable />
+    </template>
+    <el-scrollbar v-loading="state.loading" height="100%" max-height="100%" :always="false" wrap-style="padding:var(--el-card-padding)">
+      <el-tree
+        ref="orgMenuRef"
+        :data="state.orgTreeData"
+        node-key="id"
+        :props="{ children: 'children', label: 'name' }"
+        :filter-node-method="onFilterNode"
+        highlight-current
+        check-strictly
+        default-expand-all
+        render-after-expand
+        :expand-on-click-node="false"
+        v-bind="$attrs"
+        @node-click="onNodeClick"
+        @check-change="onCheckChange"
+      />
+    </el-scrollbar>
+  </el-card>
+</template>
+
+<script lang="ts" setup name="admin/org/menu">
+import { onMounted, reactive, ref, watch, nextTick } from 'vue'
+import { OrgListOutput } from '/@/api/admin/data-contracts'
+import { OrgApi } from '/@/api/admin/Org'
+import { listToTree } from '/@/utils/tree'
+import { ElTree } from 'element-plus'
+
+interface Props {
+  modelValue: number[] | null | undefined
+  selectFirstNode: boolean
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => [],
+  selectFirstNode: false,
+})
+
+const orgMenuRef = ref<InstanceType<typeof ElTree>>()
+const state = reactive({
+  loading: false,
+  filterText: '',
+  orgTreeData: [] as Array<OrgListOutput>,
+  lastKey: 0,
+})
+
+watch(
+  () => state.filterText,
+  (val) => {
+    orgMenuRef.value?.filter(val)
+  }
+)
+
+onMounted(() => {
+  initData()
+})
+
+const emits = defineEmits<{
+  (e: 'node-click', node: OrgListOutput | null): void
+  (e: 'update:modelValue', node: any[] | undefined | null): void
+}>()
+
+const onFilterNode = (value: string, data: OrgListOutput) => {
+  if (!value) return true
+  return data.name?.indexOf(value) !== -1
+}
+
+const onNodeClick = (node: OrgListOutput) => {
+  if (state.lastKey === node.id) {
+    state.lastKey = 0
+    orgMenuRef.value?.setCurrentKey(undefined)
+    emits('node-click', null)
+  } else {
+    state.lastKey = node.id as number
+    emits('node-click', node)
+  }
+}
+
+const onCheckChange = () => {
+  emits('update:modelValue', orgMenuRef.value?.getCheckedKeys())
+}
+
+const initData = async () => {
+  state.loading = true
+  const res = await new OrgApi().getList().catch(() => {
+    state.loading = false
+  })
+  state.loading = false
+  if (res?.success && res.data && res.data.length > 0) {
+    state.orgTreeData = listToTree(res.data)
+    if (state.orgTreeData.length > 0 && props.selectFirstNode) {
+      nextTick(() => {
+        const firstNode = state.orgTreeData[0]
+        orgMenuRef.value?.setCurrentKey(firstNode.id)
+        emits('node-click', firstNode)
+      })
+    }
+  } else {
+    state.orgTreeData = []
+  }
+}
+
+defineExpose({
+  orgMenuRef,
+})
+</script>

+ 145 - 0
admin.ui.plus-master/src/views/admin/application/applicationManage/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :inline="true" @submit.stop.prevent>
+        <el-form-item label="应用名称">
+          <el-input v-model="state.filter.name" placeholder="应用名称" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-row>
+        <el-col :span="6">
+          <div class="flex app appDdd" @click="onAdd">
+            <div class="flex appDdd" style="width: 100px;height: 100px;background-color: #EFEFEF;border-radius: 50%;font-size: 20px;">+</div>
+          </div>
+        </el-col>
+        <el-col :span="6" v-for="(v, k) in state.applyData" :key="k">
+          <div class="flex app">
+            <div style="display: flex;justify-content: space-between;align-items: flex-start;align-self: flex-end;">
+              <div class="flex delete" @click="onDelete(v)">X</div>
+            </div>
+            <div class="flex" style="flex: 1;">{{ v.name }}</div>
+            <div class="flex" style="height: 50px;width: 100%;">
+              <el-row style="width: 100%">
+                <el-col :span="8"><el-button style="width: 100%;height: 50px;border-radius: 0" type="primary" @click="toPermissPage(v)">权限</el-button></el-col>
+                <el-col :span="8"><el-button style="width: 98%;height: 50px;border-radius: 0;" type="primary" @click="toRolePage(v)">角色</el-button></el-col>
+                <el-col :span="8"><el-button style="width: 100%;height: 50px;border-radius: 0;" type="primary" @click="toViewPage(v)">视图</el-button></el-col>
+              </el-row>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <org-form ref="orgFormRef" :title="state.orgFormTitle" :org-tree-data="state.orgTreeData"></org-form>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/org">
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
+import { OauthApply,OauthApplyDto } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import router from "/@/router";
+import eventBus from '/@/utils/mitt'
+import { auth } from '/@/utils/authFunction'
+
+// 引入组件
+const OrgForm = defineAsyncComponent(() => import('./components/org-form.vue'))
+
+const { proxy } = getCurrentInstance() as any
+
+const orgFormRef = ref()
+
+const state = reactive({
+  loading: false,
+  orgFormTitle: '',
+  filter: {
+    name: '',
+  }as OauthApplyDto,
+  applyData: [] as Array<OauthApply>,
+})
+
+onMounted(() => {
+  onQuery()
+  eventBus.off('refreshOrg')
+  eventBus.on('refreshOrg', () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshOrg')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  const res = await new PermissionApi().getApplyInfo(state.filter).catch(() => {
+    state.loading = false
+  })
+  state.applyData=res.data
+  state.loading=false
+}
+
+const onAdd = () => {
+  state.orgFormTitle = '新增应用'
+  orgFormRef.value.open()
+}
+
+const onEdit = (row: OauthApply) => {
+  state.orgFormTitle = '编辑应用'
+  orgFormRef.value.open(row)
+}
+
+const onDelete = (row: OauthApply) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除应用【${row.name}】?`)
+    .then(async () => {
+      await new PermissionApi().applyDelete({ id: row.id }, { loading: true })
+      onQuery()
+    })
+    .catch(() => {})
+}
+const toRolePage = (row: OauthApply) => {
+  router.push({path:`/platform/application/roleManage/${row.name}`})
+}
+const toViewPage = (row: OauthApply) => {
+  router.push({path:`/platform/application/viewManage/${row.name}`})
+}
+const toPermissPage = (row: OauthApply) => {
+  router.push({path:`/platform/application/permissionManage/${row.name}`})
+}
+</script>
+
+<style scoped lang="scss">
+.flex{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+}
+.app{
+  margin-left: 10px;
+  margin-bottom: 10px;
+  height: 175px;
+  border: #7728F5 solid 2px;
+}
+.appDdd{
+  border: #7728F5 dashed 1px;
+  cursor: default;
+}
+.delete{
+  width: 25px;
+  height: 25px;
+  border: #BBBBBB solid 2px;
+  border-radius: 50%;
+  color: #BBBBBB;
+  font-size: 25px;
+  cursor: default;
+}
+</style>

+ 246 - 0
admin.ui.plus-master/src/views/admin/application/fuelingFTP/index.vue

@@ -0,0 +1,246 @@
+<template>
+  <div class="layout-pd" >
+    <el-row>
+      <!--操作-->
+      <el-col :xs="24" >
+        <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+          <div style="width: 100%;display: flex;justify-content: center;align-items: center;">
+            <h1 style="margin-bottom: 10px;font-size: 30px;">油机动态密钥生成</h1>
+          </div>
+          <div v-if="state.msgDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+            <h1 style="margin-bottom: 10px;color: #81B337;font-size: 20px;">密码申请成功</h1>
+          </div>
+          <div style="width: 100%;display: flex;justify-content: center;align-items: center;">
+            <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+              <el-form-item label="申请备注" style="width: 100%;">
+                <el-input v-model="state.filter.info" style="width: 100%;" placeholder="请输入申请备注" @keyup.enter="onQuery" />
+              </el-form-item>
+            </el-form>
+          </div>
+          <div v-if="state.pwdDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+            <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+              <el-form-item label="FTP密码" style="width: 82%;">
+                <el-input disabled v-model="state.pwd" style="width: 100%;" placeholder="FTP密码" @keyup.enter="onQuery" />
+              </el-form-item>
+              <el-form-item>
+                <el-button id="copyBtn" :data-clipboard-text="state.pwd" type="primary" @click="onCopy()">复制</el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+          <div v-if="state.timeDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+            <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+              <el-form-item label="过期时间" style="width: 100%;">
+                {{ state.filter.expirationTime }}
+              </el-form-item>
+            </el-form>
+          </div>
+          <div v-if="state.btnDisplay" style="display: flex;justify-content: center;align-items: center;">
+            <el-button style="margin-top: 10px;margin-bottom: 10px;width: 264px;height: 42px;" type="primary" @click="onQuery">申请油机FTP密码</el-button>
+          </div>
+        </el-card>
+      </el-col>
+      <!--表格-->
+      <el-col  :xs="24" >
+        <el-card style="height: 70vh" class="my-fill mt8" shadow="hover">
+          <el-table ref="multipleTableRef"
+            v-loading="state.loading"
+            stripe :data="state.tableModel"
+            row-key="id"
+            style="width: 100%" >
+            <el-table-column type="index" label="记录" width="60"/>
+            <el-table-column v-for="column in state.dynamicColumns" :key="column.prop" :prop="column.prop" :label="column.label" />
+          </el-table>
+          <div class="my-flex my-flex-end" style="margin-top: 20px">
+            <el-pagination
+              v-model:currentPage="state.pageInput.CurrentPage"
+              v-model:page-size="state.pageInput.PageSize"
+              :total="state.total"
+              :page-sizes="[10, 20, 50, 100]"
+              small
+              background
+              @size-change="onSizeChange"
+              @current-change="onCurrentChange"
+              layout="total, sizes, prev, pager, next, jumper"
+            />
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+
+</template>
+
+<script setup lang="ts" name="authorize/fuelingsdk">
+import { onMounted, reactive, ref, watch, onBeforeMount } from "vue";
+import eventBus from "/@/utils/mitt";
+import { ElMessage, ElTable } from 'element-plus'
+import { FuelingFtpAPI } from '/@/api/admin/deviceAuthorization/fuelingFTPApi'
+import { FuelFtpPswRecordDto } from '/@/api/admin/deviceAuthorization/fuelingFTPDto'
+import Clipboard from 'clipboard'
+import type {pageInput} from "/@/api/admin/shareDto/shareDto";
+import { AuthApi } from '/@/api/admin/Auth'
+
+const multipleTableRef=ref<InstanceType<typeof ElTable>>()
+/**FTP申请页面对象 */
+const state = reactive({
+  time:'',
+  /**加载显示 */
+  loading: false,
+  /**条件查询模块 */
+  filter: {
+    /**申请备注 */
+    info: "",
+    /**过期时间 */
+    expirationTime: '',
+    /**申请日期*/
+    applyDate: '',
+    /**申请人*/
+    name: ''
+  },
+   /**分页标识 */
+   pageInput:{
+    CurrentPage: 1,
+    PageSize: 10,
+  } as pageInput,
+  /**分页总数 */
+  total: 0,
+  /**表格信息 */
+  tableModel: [] as FuelFtpPswRecordDto,
+  /**动态表头 */
+  dynamicColumns: [
+    { prop: 'applyDate', label: '申请日期' },
+    { prop: 'name', label: '申请人' },
+    { prop: 'info', label: '备注' },
+    { prop: 'expirationTime', label: '密钥过期时间' }
+  ],
+  /**FTP密码 */
+  pwd: "",
+  pwdDisplay:false,
+  timeDisplay:false,
+  btnDisplay:true,
+  msgDisplay:false
+})
+
+/**初始化 */
+const init = async () => {
+  state.filter.info=''
+  state.filter.name=''
+  state.filter.applyDate=''
+  state.filter.expirationTime=''
+  state.pwd=''
+  state.btnDisplay=true
+  state.msgDisplay=false
+  state.pwdDisplay=false
+  state.timeDisplay=false
+  const res:any = await new FuelingFtpAPI().getFtpDate({...state.pageInput, filter:state.filter}).catch(() => {
+    state.loading = false
+  })
+  //console.log(res.data.list)
+  state.total=res.data.total
+  state.tableModel=res.data.list
+}
+
+onMounted(() => {
+  init()
+  eventBus.off('refreshView')
+  eventBus.on('refreshView', async () => {
+    init()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshView')
+})
+
+/**
+ * 监听时间变换
+ */
+watch(() => state.time, (newVal ) => {
+  if(newVal.length === 0){
+    return
+  }
+  state.filter.expirationTime = newVal?.[0].toString()
+})
+/**复制FTP密码 */
+const onCopy=()=>{
+  const clipboard=new Clipboard('#copyBtn')
+  clipboard.on('success',()=>{
+    ElMessage.success('复制成功')
+    clipboard.destroy()
+  })
+  clipboard.on('error',()=>{
+    ElMessage.error('复制失败')
+    clipboard.destroy()
+  })
+}
+/**申请FTP密码 */
+const onQuery =async () => {
+  if(state.filter.info===''){
+    ElMessage.error('请输入申请备注')
+  }else{
+    const res=await new FuelingFtpAPI().getFtpPwd().catch()
+    state.pwd=res.data
+    state.msgDisplay=true
+    state.pwdDisplay=true
+    state.timeDisplay=true
+    state.btnDisplay=false
+    const date=new Date()
+    let month=padZero(date.getMonth()+1)
+    let day=padZero(date.getDate())
+    let hour=padZero(date.getHours())
+    let minute=padZero(date.getMinutes())
+    let second=padZero(date.getSeconds())
+    state.filter.applyDate=date.getFullYear()+'-'+month+'-'+day+' '+hour+':'+minute+':'+second
+    state.filter.expirationTime=date.getFullYear()+'-'+month+'-'+day+' 23:59:59'
+    const name:any=await new AuthApi().getUserProfile()
+    state.filter.name=name.data.name
+    await new FuelingFtpAPI().insertFtpDate(state.filter)
+    const data:any = await new FuelingFtpAPI().getFtpDate({...state.pageInput, filter:state.filter}).catch(() => {
+      state.loading = false
+    })
+    state.total=data.data.total
+    state.tableModel=data.data.list
+  }
+}
+/**将Filter对象成.的连接方式*/
+const flattenObject = (obj, parentKey = '') => {
+  const result = {};
+  for (const key in obj) {
+    if (obj.hasOwnProperty(key)) {
+      const newKey = parentKey ? `${parentKey}.${key}` : key;
+      if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+        const flattened = flattenObject(obj[key], newKey);
+        Object.assign(result, flattened);
+      } else {
+        result[newKey] = obj[key];
+      }
+    }
+  }
+
+  return result;
+}
+/**页条数变化*/
+const onSizeChange = () => {
+  init()
+}
+
+/**页数变化*/
+const onCurrentChange = () =>{
+  init()
+}
+const padZero=(num:any)=>{
+  return num<10?'0'+num:num
+}
+</script>
+
+<style scoped lang="scss">
+.my-el-link {
+  font-size: 12px;
+}
+.el-form .el-col.mb20 {
+  margin: 0 !important;
+}
+.el-input, .el-select {
+  width: 240px;
+}
+</style>

+ 264 - 0
admin.ui.plus-master/src/views/admin/application/fuelingFTPofDate/index.vue

@@ -0,0 +1,264 @@
+<template>
+    <div class="layout-pd" >
+      <el-row>
+        <!--操作-->
+        <el-col :xs="24" >
+          <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+            <div style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <h1 style="margin-bottom: 10px;font-size: 30px;">油机非当天动态密钥生成</h1>
+            </div>
+            <div v-if="state.msgDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <h1 style="margin-bottom: 10px;color: #81B337;font-size: 20px;">密码申请成功</h1>
+            </div>
+            <div style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+                <el-form-item label="密钥使用日期" style="width: 100%;">
+                  <el-input  type="date" v-model="state.filter.useDate" style="width: 30%;"  @keyup.enter="onQuery" />
+                </el-form-item>
+              </el-form>
+            </div>            
+            <div style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+                <el-form-item label="申请备注" style="width: 100%;">
+                  <el-input v-model="state.filter.info" style="width: 100%;" placeholder="请输入申请备注" @keyup.enter="onQuery" />
+                </el-form-item>
+              </el-form>
+            </div>
+            <div v-if="state.pwdDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+                <el-form-item label="FTP密码" style="width: 82%;">
+                  <el-input disabled v-model="state.pwd" style="width: 100%;" placeholder="FTP密码" @keyup.enter="onQuery" />
+                </el-form-item>
+                <el-form-item>
+                  <el-button id="copyBtn" :data-clipboard-text="state.pwd" type="primary" @click="onCopy()">复制</el-button>
+                </el-form-item>
+              </el-form>
+            </div>
+            <div v-if="state.timeDisplay" style="width: 100%;display: flex;justify-content: center;align-items: center;">
+              <el-form :inline="true" @submit.stop.prevent style="width: 60%;">
+                <el-form-item label="过期时间" style="width: 100%;">
+                  {{ state.filter.expirationTime }}
+                </el-form-item>
+              </el-form>
+            </div>
+            <div v-if="state.btnDisplay" style="display: flex;justify-content: center;align-items: center;">
+              <el-button style="margin-top: 10px;margin-bottom: 10px;width: 264px;height: 42px;" type="primary" @click="onQuery">申请油机FTP密码</el-button>
+            </div>
+          </el-card>
+        </el-col>
+        <!--表格-->
+        <el-col  :xs="24" >
+          <el-card style="height: 65vh" class="my-fill mt8" shadow="hover">
+            <el-table ref="multipleTableRef"
+              v-loading="state.loading"
+              stripe :data="state.tableModel"
+              row-key="id"
+              style="width: 100%" >
+              <el-table-column type="index" label="记录" width="60"/>
+              <el-table-column v-for="column in state.dynamicColumns" :key="column.prop" :prop="column.prop" :label="column.label" />
+            </el-table>
+            <div class="my-flex my-flex-end" style="margin-top: 20px">
+              <el-pagination
+                v-model:currentPage="state.pageInput.CurrentPage"
+                v-model:page-size="state.pageInput.PageSize"
+                :total="state.total"
+                :page-sizes="[10, 20, 50, 100]"
+                small
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                layout="total, sizes, prev, pager, next, jumper"
+              />
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+  
+  </template>
+  
+  <script setup lang="ts" name="authorize/fuelingsdk">
+  import { onMounted, reactive, ref, watch, onBeforeMount } from "vue";
+  import eventBus from "/@/utils/mitt";
+  import { ElMessage, ElTable } from 'element-plus'
+  import { FuelingFtpAPI } from '/@/api/admin/deviceAuthorization/fuelingFTPApi'
+  import { FuelFtpPswRecordDto } from '/@/api/admin/deviceAuthorization/fuelingFTPDto'
+  import Clipboard from 'clipboard'
+  import type {pageInput} from "/@/api/admin/shareDto/shareDto";
+  import { AuthApi } from '/@/api/admin/Auth'
+  
+  const multipleTableRef=ref<InstanceType<typeof ElTable>>()
+  /**FTP申请页面对象 */
+  const state = reactive({
+    time:'',
+    /**加载显示 */
+    loading: false,
+    /**条件查询模块 */
+    filter: {
+      /**申请备注 */
+      info: "",
+      /**过期时间 */
+      expirationTime: '',
+      /**使用日期*/
+      useDate: '',      
+      /**申请日期*/
+      applyDate: '',
+      /**申请人*/
+      name: ''
+    },
+     /**分页标识 */
+     pageInput:{
+      CurrentPage: 1,
+      PageSize: 10,
+    } as pageInput,
+    /**分页总数 */
+    total: 0,
+    /**表格信息 */
+    tableModel: [] as FuelFtpPswRecordDto,
+    /**动态表头 */
+    dynamicColumns: [
+      { prop: 'applyDate', label: '申请日期' },
+      { prop: 'name', label: '申请人' },
+      { prop: 'info', label: '备注' },
+      { prop: 'useDate', label: '密钥使用日期' },
+      { prop: 'expirationTime', label: '密钥过期时间' }
+    ],
+    /**FTP密码 */
+    pwd: "",
+    pwdDisplay:false,
+    timeDisplay:false,
+    btnDisplay:true,
+    msgDisplay:false
+  })
+  
+  /**初始化 */
+  const init = async () => {
+    state.filter.info=''
+    state.filter.name=''
+    state.filter.applyDate=''
+    state.filter.expirationTime=''
+    state.filter.useDate=''
+    state.pwd=''
+    state.btnDisplay=true
+    state.msgDisplay=false
+    state.pwdDisplay=false
+    state.timeDisplay=false
+    const res:any = await new FuelingFtpAPI().getFtpDate({...state.pageInput, filter:state.filter}).catch(() => {
+      state.loading = false
+    })
+    //console.log(res.data.list)
+    state.total=res.data.total
+    state.tableModel=res.data.list
+  }
+  
+  onMounted(() => {
+    init()
+    eventBus.off('refreshView')
+    eventBus.on('refreshView', async () => {
+      init()
+    })
+  })
+  
+  onBeforeMount(() => {
+    eventBus.off('refreshView')
+  })
+  
+  /**
+   * 监听时间变换
+   */
+  watch(() => state.time, (newVal ) => {
+    if(newVal.length === 0){
+      return
+    }
+    state.filter.expirationTime = newVal?.[0].toString()
+  })
+  /**复制FTP密码 */
+  const onCopy=()=>{
+    const clipboard=new Clipboard('#copyBtn')
+    clipboard.on('success',()=>{
+      ElMessage.success('复制成功')
+      clipboard.destroy()
+    })
+    clipboard.on('error',()=>{
+      ElMessage.error('复制失败')
+      clipboard.destroy()
+    })
+  }
+  /**申请FTP密码 */
+  const onQuery =async () => {
+    if(state.filter.useDate==='')
+    {
+        ElMessage.error('请选择密钥使用日期')  
+    }
+    else if(state.filter.info==='')
+    {
+      ElMessage.error('请输入申请备注')
+    }
+    else{
+      const res=await new FuelingFtpAPI().getFtpPwdofDate({dt:state.filter.useDate}).catch()
+      state.pwd=res.data
+      state.msgDisplay=true
+      state.pwdDisplay=true
+      state.timeDisplay=true
+      state.btnDisplay=false
+      const date=new Date()
+      let month=padZero(date.getMonth()+1)
+      let day=padZero(date.getDate())
+      let hour=padZero(date.getHours())
+      let minute=padZero(date.getMinutes())
+      let second=padZero(date.getSeconds())
+      state.filter.applyDate=date.getFullYear()+'-'+month+'-'+day+' '+hour+':'+minute+':'+second
+      state.filter.expirationTime=state.filter.useDate+' 23:59:59'
+      const name:any=await new AuthApi().getUserProfile()
+      state.filter.name=name.data.name
+      await new FuelingFtpAPI().insertFtpDate(state.filter)
+      const data:any = await new FuelingFtpAPI().getFtpDate({...state.pageInput, filter:state.filter}).catch(() => {
+        state.loading = false
+      })
+      state.total=data.data.total
+      state.tableModel=data.data.list
+    }
+  }
+  /**将Filter对象成.的连接方式*/
+  const flattenObject = (obj, parentKey = '') => {
+    const result = {};
+    for (const key in obj) {
+      if (obj.hasOwnProperty(key)) {
+        const newKey = parentKey ? `${parentKey}.${key}` : key;
+        if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+          const flattened = flattenObject(obj[key], newKey);
+          Object.assign(result, flattened);
+        } else {
+          result[newKey] = obj[key];
+        }
+      }
+    }
+  
+    return result;
+  }
+  /**页条数变化*/
+  const onSizeChange = () => {
+    init()
+  }
+  
+  /**页数变化*/
+  const onCurrentChange = () =>{
+    init()
+  }
+  const padZero=(num:any)=>{
+    return num<10?'0'+num:num
+  }
+  </script>
+  
+  <style scoped lang="scss">
+  .my-el-link {
+    font-size: 12px;
+  }
+  .el-form .el-col.mb20 {
+    margin: 0 !important;
+  }
+  .el-input, .el-select {
+    width: 240px;
+  }
+  </style>
+  

+ 14 - 0
admin.ui.plus-master/src/views/admin/application/fuelingsdk/README.md

@@ -0,0 +1,14 @@
+# 简介
+
+	authorize目录是设备授权管理菜单
+
+## components目录
+
+### form-edit.vue
+
+	表单组件
+
+## fueLingSdk目录
+
+	设备授权管理 - 油机SDK授权页面 
+

+ 109 - 0
admin.ui.plus-master/src/views/admin/application/fuelingsdk/components/form-audit.vue

@@ -0,0 +1,109 @@
+<template>
+  <div>
+    <el-dialog title="审核" v-model="isShowDialog" draggable width="400px">
+      <el-form ref="formRef" :model="formData.auditData" :rules="rules" >
+        <el-form-item label="失效时间" prop="expiedTime">
+          <el-date-picker
+                    v-model="formData.auditData.expiedTime"
+                    type="datetime"
+                    value-format="YYYY-MM-DD HH:mm:ss"
+                    placeholder="请选择失效时间" >
+          </el-date-picker>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" icon="ele-CircleClose" size="default">取 消</el-button>
+          <el-button type="primary" @click="confirmAudit">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {reactive, ref, watch} from "vue";
+import {oilSdkTableModel,FueilingSdkAuthInput} from "/src/api/admin/deviceAuthorization/oilSdkAuthorDto";
+import {OilSdkAuthorAPI} from "/src/api/admin/deviceAuthorization/oilSdkAuthor";
+import eventBus from "/src/utils/mitt";
+import {FormRules} from "element-plus";
+
+/**数据对象 */
+const formData = reactive({
+  loading: false,
+  //isShowDialog: false,
+  auditData:[] as FueilingSdkAuthInput
+})
+/**有效时间选择校验*/
+const validateDate = (rule: any, value: any, callback: any) => {
+  if(new Date(value).getTime() > Date.now()){
+    callback()
+  }else{
+    callback(new Error('选择的时间已失效'))
+  }
+}
+
+/**表单校验*/
+const rules = reactive<FormRules>({
+  expiedTime: [
+    { type: 'date', required: true, message: '请选择失效期', trigger: 'blur' },
+    { validator: validateDate, trigger: 'blur' }
+  ],
+})
+const isShowDialog = ref(false);
+const formRef = ref()
+
+const openDialog = (val:any[]) => {
+  //console.log(val)
+  formData.auditData = JSON.parse(JSON.stringify(val))
+  //console.log(formData.auditData)
+  //formData.isShowDialog = true
+  //formData.isShowDialog = true
+  isShowDialog.value = true;
+}
+
+const onCancel = () => {
+  isShowDialog.value=false
+}
+
+const confirmAudit =async () => {
+  formRef.value.validate(async (valid: boolean) =>{
+    if(!valid) return
+    formData.auditData=formData.auditData.map(item=>{
+      item.expiedTime=formData.auditData.expiedTime
+      item.state = '正常' ? 1 : 0 ?? 0
+      return item
+    })
+    formData.loading = true
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
+    let res = {} as any
+    res = await new OilSdkAuthorAPI().updateForms(formData.auditData,{ showSuccessMessage: true }).catch(() => {
+      formData.loading = false
+    })
+
+    if(res?.success){
+      //console.log('2')
+      //console.log(formData.auditData)
+      eventBus.emit('refreshView')
+      isShowDialog.value = false
+      formData.auditData = {} as oilSdkTableModel
+    }
+
+    formData.loading = false
+  })
+}
+
+/***监听弹窗关   闭表单验证*/
+watch(() => isShowDialog.value,(newVal) => {
+  if(newVal) formRef.value?.resetFields()
+})
+
+defineExpose({
+  openDialog,
+})
+
+
+</script>
+<style scoped lang="scss">
+
+</style>

+ 210 - 0
admin.ui.plus-master/src/views/admin/application/fuelingsdk/components/form-edit.vue

@@ -0,0 +1,210 @@
+<template>
+  <div class="admin-authorize-editInfo">
+    <el-dialog :title="formData.titleText" v-model="formData.isShowDialog" draggable width="769px">
+      <el-form :model="formData.editData"
+               :rules="rules"
+               ref="formRef"
+               v-loading="formData.loading"
+               size="default"
+               label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="油站名称" prop="oilStation">
+              <el-input v-model="formData.editData.oilStation" placeholder="请输入油站名称" clearable></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="项目名称" prop="projectName">
+              <el-input v-model="formData.editData.projectName" placeholder="请输入项目名称" clearable></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="设备SN" prop="sn">
+              <el-input v-model="formData.editData.sn" placeholder="请输入设备SN号" clearable></el-input>
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="授权码" prop="key">
+              <el-input v-model="formData.editData.key" placeholder="请输入授权码" clearable></el-input>
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="失效时间" prop="expiedTime">
+              <el-date-picker
+                v-model="formData.editData.expiedTime"
+                type="datetime"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                placeholder="选择失效时间"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="状态" prop="state">
+              <el-select v-model="formData.editData.state" class="m-2" placeholder="选择状态" >
+                <el-option
+                  v-for="item in formData.options"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="formData.editData.remark" placeholder="请输入备注" rows="6" clearable type="textarea"></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" icon="ele-CircleClose" size="default">取 消</el-button>
+          <el-button type="primary" :icon="formData.editIcon" @click="onSubmit" size="default">{{formData.buttonText}}</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {reactive, ref, watch} from "vue";
+import {oilSdkTableModel} from "/src/api/admin/deviceAuthorization/oilSdkAuthorDto";
+import {OilSdkAuthorAPI} from "/src/api/admin/deviceAuthorization/oilSdkAuthor";
+import eventBus from "/src/utils/mitt";
+import {FormRules} from "element-plus";
+
+enum Form {
+  ADD,
+  UPDATE
+}
+
+/**数据对象 */
+const formData = reactive({
+  loading: false,
+  formType: Form,
+  titleText:'',
+  editIcon:'',
+  buttonText:'',
+  isShowDialog: false,
+  editData:{} as oilSdkTableModel,
+  // /**动态表头 */
+  // dynamicColumns: [
+  //   { prop: 'oilStation', label: '油站名称' },
+  //   { prop: 'projectName', label: '项目名称' },
+  //   { prop: 'sn', label: '设备SN号' },
+  //   { prop: 'key', label: '授权码' },
+  //   { prop: 'expiedTime', label: '过期时间' },
+  //   { prop: 'formData', label: '状态' },
+  //   { prop: 'remark', label: '备注' },
+  // ],
+  options:[
+    {label:'正常',value:'正常'},
+    {label:'未授权',value:'未授权'},
+  ]
+})
+
+/**有效时间选择校验*/
+const validateDate = (rule: any, value: any, callback: any) => {
+  if(new Date(value).getTime() > Date.now()){
+    callback()
+  }else{
+    callback(new Error('选择的时间已失效'))
+  }
+}
+
+/**表单校验*/
+const rules = reactive<FormRules>({
+  oilStation: [
+    { required: true, message: '请输入油站名称', trigger: 'blur' },
+  ],
+  projectName: [
+    { required: true, message: '请输入项目名称', trigger: 'blur' },
+  ],
+  sn: [
+    { required: true, message: '请输入设备SN号', trigger: 'blur' },
+  ],
+  key: [
+    { required: true, message: '请输入设备授权码', trigger: 'blur' },
+  ],
+  expiedTime: [
+    { type: 'date', required: true, message: '请选择失效期', trigger: 'blur' },
+    { validator: validateDate, trigger: 'blur' }
+  ],
+  state: [
+    { required: true, message: '请选择状态', trigger: 'blur' },
+  ],
+})
+
+
+const formRef = ref()
+
+const openDialog = (val) => {
+  if(val){
+    formData.editData = JSON.parse(JSON.stringify(val))
+    formData.titleText = '更新授权'
+    formData.buttonText = '更新'
+    //更新操作
+    formData.formType = Form.UPDATE
+    formData.editIcon = 'ele-Refresh'
+  }else {
+    formData.editData = {} as oilSdkTableModel
+    formData.titleText = '添加授权'
+    formData.buttonText = '添加'
+    //添加操作
+    formData.formType = Form.ADD
+    formData.editIcon = 'ele-CirclePlus'
+  }
+  formData.isShowDialog = true
+  formData.isShowDialog = true
+}
+
+const onCancel = () => {
+  formData.editData = {} as oilSdkTableModel
+  formData.isShowDialog = false
+}
+
+/***监听弹窗关   闭表单验证*/
+watch(() => formData.isShowDialog,(newVal) => {
+  if(newVal) formRef.value?.resetFields()
+})
+
+const onSubmit =  () => {
+  formRef.value.validate(async (valid: boolean) =>{
+    if(!valid) return
+
+    formData.loading = true
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
+    const { projectName, state, ...newData} = formData.editData
+    let res = {} as any
+    if(formData.formType === Form.UPDATE){
+      res = await new OilSdkAuthorAPI().updateForm({project: formData.editData.projectName, state: formData.editData.state === '正常' ? 1 : 0 ?? 0,  ...newData},{ showSuccessMessage: true }).catch(() => {
+        formData.loading = false
+      })
+    }else if(formData.formType === Form.ADD){
+      res = await new OilSdkAuthorAPI().addForm({project: formData.editData.projectName, state: formData.editData.state === '正常' ? 1 : 0 ?? 0,  ...newData},{ showSuccessMessage: true }).catch(() => {
+        formData.loading = false
+      })
+    }else {
+      formData.loading = false
+      return
+    }
+
+    if(res?.success){
+      console.log('2')
+      eventBus.emit('refreshView')
+      formData.isShowDialog = false
+      formData.editData = {} as oilSdkTableModel
+    }
+
+    formData.loading = false
+  })
+}
+
+defineExpose({
+  openDialog,
+})
+</script>
+<style scoped lang="scss">
+
+</style>

+ 346 - 0
admin.ui.plus-master/src/views/admin/application/fuelingsdk/index.vue

@@ -0,0 +1,346 @@
+<template>
+  <div class="layout-pd">
+    <el-row>
+      <!--操作-->
+      <el-col :xs="24">
+        <el-card class="mt8" shadow="hover">
+          <el-form :model="sdkData.Filter" @submit.stop.prevent>
+            <el-form-item prop="name" style="width: 100%">
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="油站名称">
+                  <el-input v-model="sdkData.Filter.OilStation" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="项目名称">
+                  <el-input v-model="sdkData.Filter.Project" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="SN">
+                  <el-input v-model="sdkData.Filter.SN" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="授权码">
+                  <el-input v-model="sdkData.Filter.Key" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="失效时间">
+                  <el-date-picker v-model="sdkData.time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss"
+                    range-separator="To" start-placeholder="Start date" end-placeholder="End date" />
+                </el-form-item>
+              </el-col>
+            </el-form-item>
+          </el-form>
+
+          <hr>
+
+          <!-- 按钮  -->
+          <el-row justify="space-between" class="submit-button">
+            <el-row>
+              <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+              <el-button type="primary" icon="ele-RefreshRight" @click="onReset"> 重置 </el-button>
+            </el-row>
+            <el-row>
+              <el-button type="primary" icon="ele-CirclePlus" @click="onAdd"> 添加 </el-button>
+              <el-button type="primary" icon="ele-Tickets" @click="onAuditRecord()"> 审核 </el-button>
+              <el-button type="primary" icon="ele-UploadFilled" @click="uploadFiles"> 批量导入 </el-button>
+              <el-button type="primary" icon="ele-Document"
+                @click="exportTable(sdkData.tableModel, sdkData.dynamicColumns)"> 导出表格 </el-button>
+            </el-row>
+          </el-row>
+        </el-card>
+      </el-col>
+      <!--表格-->
+      <el-col :xs="24">
+        <el-card style="height: 70vh" class="my-fill mt8" shadow="hover">
+          <el-table ref="multipleTableRef" v-loading="sdkData.loading" stripe :data="sdkData.tableModel" row-key="id"
+            style="width: 100%" @row-click="onOilSdkRowClick">
+            <el-table-column type="selection" width="50"></el-table-column>
+            <el-table-column v-for="column in sdkData.dynamicColumns" :key="column.prop" :prop="column.prop"
+              :label="column.label" />
+            <el-table-column label="操作" fixed="right" header-align="center" align="center" class="right-operation"
+              width="100">
+              <template #default="{ row }">
+                <el-link class="my-el-link mr12 ml12" type="primary" icon="ele-Edit" @click="editTableData(row)"
+                  :underline="false" target="_blank">编辑</el-link>
+                <!-- <el-link
+                  class="my-el-link mr12 ml12"
+                  v-if="showAudit(row)"
+                  type="primary"
+                  icon="ele-Tickets"
+                  size="small"
+                  @click="onAuditRecord(row)"
+                  :underline="false"
+                  target="_blank"
+                >审核</el-link> -->
+              </template>
+            </el-table-column>
+          </el-table>
+          <div class="my-flex my-flex-end" style="margin-top: 20px">
+            <el-pagination v-model:currentPage="sdkData.pageInput.CurrentPage"
+              v-model:page-size="sdkData.pageInput.PageSize" :total="sdkData.total"
+              :page-sizes="[10000, 20000, 50000, 100000]" small background @size-change="onSizeChange"
+              @current-change="onCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+    <EditDialog ref="editDialogRef" />
+    <AuditDialog ref="auditDialogRef" />
+  </div>
+
+</template>
+
+<script setup lang="ts" name="authorize/fuelingsdk">
+import { defineAsyncComponent, onMounted, reactive, ref, watch, onBeforeMount, getCurrentInstance } from "vue";
+import { OilSdkAuthorDto, oilSdkFilterModel, oilSdkTableModel, oilSdkAuthorPageOutput, FueilingSdkAuthInput } from "/@/api/admin/deviceAuthorization/oilSdkAuthorDto";
+import { OilSdkAuthorAPI } from "/@/api/admin/deviceAuthorization/oilSdkAuthor";
+import type { pageInput } from "/@/api/admin/shareDto/shareDto";
+import eventBus from "/@/utils/mitt";
+import * as ExcelJS from 'exceljs';
+import * as FileSaver from 'file-saver';
+import { ElTable } from 'element-plus'
+/**引入组件*/
+const EditDialog = defineAsyncComponent(() => import('/src/views/admin/authorize/fuelingsdk/components/form-edit.vue'))
+const AuditDialog = defineAsyncComponent(() => import('/src/views/admin/authorize/fuelingsdk/components/form-audit.vue'))
+
+const editDialogRef = ref()
+const auditDialogRef = ref()
+
+const multipleTableRef = ref<InstanceType<typeof ElTable>>()
+const { proxy } = getCurrentInstance() as any
+
+/**sdk授权页面对象 */
+const sdkData = reactive({
+  time: '',
+  /**加载显示 */
+  loading: false,
+  /**条件查询模块 */
+  Filter: {
+    /**油站名称 */
+    OilStation: "",
+    /**项目名称 */
+    Project: "",
+    /**设备SN */
+    SN: "",
+    /**授权码*/
+    Key: "",
+    /**开始的时间 */
+    BeginTime: null,
+    /**开始的时间 */
+    EndTime: null,
+  } as oilSdkFilterModel,
+  /**表格信息 */
+  tableModel: [] as oilSdkTableModel,
+  /**动态表头 */
+  dynamicColumns: [
+    { prop: 'guid', label: '编号' },
+    { prop: 'oilStation', label: '油站名称' },
+    { prop: 'projectName', label: '项目名称' },
+    { prop: 'sn', label: '设备SN号' },
+    { prop: 'key', label: '授权码' },
+    { prop: 'expiedTime', label: '失效时间' },
+    { prop: 'state', label: '状态' },
+    { prop: 'remark', label: '备注' },
+  ],
+  /**分页标识 */
+  pageInput: {
+    CurrentPage: 1,
+    PageSize: 10000,
+  } as pageInput,
+  /**分页总数 */
+  total: 0,
+} as OilSdkAuthorDto)
+
+/**初始化 */
+const init = async () => {
+  sdkData.loading = true
+  const query = flattenObject({ Filter: sdkData.Filter })
+  const res: any = await new OilSdkAuthorAPI().getPageData({ ...sdkData.pageInput, ...query }).catch(() => {
+    sdkData.loading = false
+  })
+  sdkData.tableModel = res?.data?.list ?? []
+  sdkData.total = res?.data?.total ?? 0
+  sdkData.loading = false
+}
+
+onMounted(() => {
+  init()
+  eventBus.off('refreshView')
+  eventBus.on('refreshView', async () => {
+    init()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshView')
+})
+
+/**
+ * 监听时间变换
+ */
+watch(() => sdkData.time, (newVal) => {
+  if (newVal.length === 0) {
+    return
+  }
+  sdkData.Filter.BeginTime = newVal?.[0].toString()
+  sdkData.Filter.EndTime = newVal?.[1].toString()
+})
+
+/**条件查询 */
+const onQuery = () => {
+  init()
+}
+
+/**重置查询条件 */
+const resetQuery = () => {
+  sdkData.Filter.OilStation = ''
+  sdkData.Filter.Project = ''
+  sdkData.Filter.SN = ''
+  sdkData.Filter.Key = ''
+  sdkData.Filter.BeginTime = ''
+  sdkData.Filter.EndTime = ''
+  sdkData.time = ''
+  sdkData.pageInput.CurrentPage = 1
+}
+
+/**重置 */
+const onReset = () => {
+  resetQuery()
+  init()
+}
+
+/**添加*/
+const onAdd = () => {
+  editDialogRef.value.openDialog()
+}
+
+/**页条数变化*/
+const onSizeChange = () => {
+  init()
+}
+
+/**页数变化*/
+const onCurrentChange = () => {
+  init()
+}
+
+/**批量导入*/
+const uploadFiles = () => {
+
+}
+
+/**导出表格*/
+const exportTable = async (data: any[], columns: { prop: string, label: string }[]) => {
+  // 创建一个新的工作簿
+  const workbook = new ExcelJS.Workbook();
+  const worksheet = workbook.addWorksheet('Sheet1');
+
+  // 添加表头(不包括名为 "guid" 的表头列)
+  const headers = columns
+    .filter(column => column.prop !== 'guid')  // 过滤掉名为 "guid" 的表头列
+    .map(column => column.label);
+  const headerRow = worksheet.addRow(headers);
+
+  // 添加数据行(不包括 "guid" 的数据列)
+  data.forEach((item) => {
+    const row = columns
+      .filter(column => column.prop !== 'guid')  // 过滤掉名为 "guid" 的数据列
+      .map(column => item[column.prop]);
+    worksheet.addRow(row);
+  });
+
+  // 设置数据行样式
+  worksheet.eachRow({ includeEmpty: true }, row => {
+    row.eachCell(cell => {
+      // 设置单元格样式
+      cell.font = { bold: false }; // 设置字体为普通样式
+      cell.border = { top: { style: 'thin' }, bottom: { style: 'thin' } }; // 设置上下边框为细线
+      cell.alignment = { vertical: 'middle', horizontal: 'center' }; // 将内容垂直居中、水平居中
+      // 设置单元格高度
+      row.height = 25; // 设置每一行的高度为 25
+    });
+  });
+
+  // 设置表头样式
+  headerRow.font = { bold: true }; // 设置字体为粗体
+
+  // 设置整个表格的边框样式
+  worksheet.eachRow({ includeEmpty: true }, row => {
+    row.eachCell(cell => {
+      if (cell.row === 1) {
+        // 设置表头单元格的边框为细线
+        cell.border = { left: { style: 'thin' }, right: { style: 'thin' }, top: { style: 'thin' }, bottom: { style: 'thin' } };
+        // 设置表头单元格背景颜色
+        cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: '409eff' } }
+      } else {
+        // 设置数据行单元格的边框为细线
+        cell.border = { left: { style: 'thin' }, right: { style: 'thin' }, bottom: { style: 'thin' } };
+      }
+    });
+  });
+
+  // 调整单元格大小
+  worksheet.columns.forEach(column => {
+    column.width = 25; // 设置每列的宽度为 25
+  });
+
+
+  // 生成 Excel 文件
+  const buffer = await workbook.xlsx.writeBuffer();
+
+  // 将文件保存到本地
+  const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+  FileSaver.saveAs(blob, '油气SDK授权数据.xlsx');
+}
+
+/**将Filter对象成.的连接方式*/
+const flattenObject = (obj, parentKey = '') => {
+  const result = {};
+  for (const key in obj) {
+    if (obj.hasOwnProperty(key)) {
+      const newKey = parentKey ? `${parentKey}.${key}` : key;
+      if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+        const flattened = flattenObject(obj[key], newKey);
+        Object.assign(result, flattened);
+      } else {
+        result[newKey] = obj[key];
+      }
+    }
+  }
+
+  return result;
+}
+
+/**编辑弹窗 */
+const editTableData = (row) => {
+  editDialogRef.value.openDialog(row)
+}
+/**审核弹窗 */
+const onAuditRecord = () => {
+  const selectionRows = multipleTableRef.value!.getSelectionRows() as FueilingSdkAuthInput
+  //console.log(selectionRows)
+
+  if (!((selectionRows.length as number) > 0)) {
+    proxy.$modal.msgWarning('请选择要审核的数据')
+    return
+  } else {
+    auditDialogRef.value.openDialog(selectionRows)
+  }
+
+}
+const onOilSdkRowClick = (row: FueilingSdkAuthInput) => {
+  // TODO: improvement typing when refactor table
+  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+  // @ts-expect-error
+  multipleTableRef.value!.toggleRowSelection(row, undefined)
+}
+</script>
+
+<style scoped lang="scss">
+@import '/@/theme/tableStyle.scss';
+
+</style>

+ 115 - 0
admin.ui.plus-master/src/views/admin/application/operationLog/index.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" >
+      <el-form :model="state.filterModel" :inline="true" @submit.stop.prevent>
+        <el-form-item prop="name">
+          <el-input v-model="state.filterModel.createdUserName" placeholder="操作账号" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table ref="tableRef" v-loading="state.loading" :data="state.oprationLogListData" row-key="id" style="width: 100%" stripe>
+        <el-table-column prop="createdUserName" label="操作账号" >
+          <template #default="{ row }"> {{ row.createdUserName }}</template>
+        </el-table-column>
+        <el-table-column prop="operationType" label="操作类型" >
+          <template #default="{ row }"> {{ row.operationType }}</template>
+        </el-table-column>
+        <el-table-column prop="moduleName" label="模块名称" >
+          <template #default="{ row }"> {{ row.moduleName }}</template>
+        </el-table-column>
+        <el-table-column prop="operationContent" label="描述" >
+          <template #default="{ row }"> {{ row.operationContent }}</template>
+        </el-table-column>
+        <el-table-column prop="beforeState" label="操作前" >
+          <template #default="{ row }"> {{ row.beforeState }}</template>
+        </el-table-column>
+        <el-table-column prop="afterState" label="操作后" >
+          <template #default="{ row }"> {{ row.afterState }}</template>
+        </el-table-column>
+        <el-table-column prop="result" label="操作结果" >
+          <template #default="{ row }"> {{ row.result }}</template>
+        </el-table-column>
+        <el-table-column prop="createdTime" label="操作时间" >
+          <template #default="{ row }"> {{ row.createdTime }}</template>
+        </el-table-column>
+      </el-table>
+      <div class="my-flex my-flex-end" style="margin-top: 20px">
+        <el-pagination
+          v-model:currentPage="state.pageInput.currentPage"
+          v-model:page-size="state.pageInput.pageSize"
+          :total="state.total"
+          :page-sizes="[10, 20, 50, 100]"
+          small
+          background
+          @size-change="onSizeChange"
+          @current-change="onCurrentChange"
+          layout="total, sizes, prev, pager, next, jumper"
+        />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/oprationLog">
+import { reactive, onMounted, ref } from 'vue'
+import { OprationLogListOutput, PageInputLogGetPageDto,PageInputOperationGetPageDto,OauthOperationRecordGetPageOutPut } from '/@/api/admin/data-contracts'
+import { OprationLogApi } from '/@/api/admin/OprationLog'
+import { PermissionApi } from '/@/api/admin/Permission'
+import dayjs from 'dayjs'
+import type { TableInstance } from 'element-plus'
+
+const tableRef = ref<TableInstance>()
+
+const state = reactive({
+  loading: false,
+  oprationLogFormTitle: '',
+  filterModel: {
+    createdUserName: '',
+  },
+  total: 0,
+  pageInput: {
+    currentPage: 1,
+    pageSize: 20,
+  } as PageInputOperationGetPageDto,
+  oprationLogListData: [] as Array<OauthOperationRecordGetPageOutPut>,
+  oprationLogLogsTitle: '',
+})
+
+onMounted(() => {
+  onQuery()
+})
+
+const formatterTime = (row: any, column: any, cellValue: any) => {
+  return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
+}
+
+const onQuery = async () => {
+  state.loading = true
+  state.pageInput.filter = state.filterModel
+  const res = await new PermissionApi().getOperationInfo(state.pageInput).catch(() => {
+    state.loading = false
+  })
+
+  state.oprationLogListData = res?.data?.list ?? []
+  state.total = res?.data?.total ?? 0
+  state.loading = false
+}
+
+const onSizeChange = (val: number) => {
+  state.pageInput.pageSize = val
+  onQuery()
+}
+
+const onCurrentChange = async (val: number) => {
+  state.pageInput.currentPage = val
+  await onQuery()
+  tableRef.value?.setScrollTop(0)
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 243 - 0
admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-dot-form.vue

@@ -0,0 +1,243 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用" prop="label">
+              <el-input v-model="state.app" clearable disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="上级菜单">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="permissionTreeData"
+                node-key="id"
+                check-strictly
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                filterable
+                class="w100"
+              />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="API接口">
+              <el-tree-select
+                v-model="form.apiIds"
+                :data="state.apiTreeData"
+                node-key="id"
+                :props="{ label: 'path' }"
+                render-after-expand
+                fit-input-width
+                clearable
+                filterable
+                multiple
+                collapse-tags
+                collapse-tags-tooltip
+                :filter-node-method="onApiFilterNode"
+                class="w100"
+                :default-expanded-keys="state.expandRowKeys"
+                @current-change="onApiCurrentChange"
+              >
+                <template #default="{ data }">
+                  <span class="my-flex my-flex-between">
+                    <span>{{ data.label }}</span>
+                    <span class="my-line-1 my-mlr-12" :title="data.path">
+                      {{ data.path }}
+                    </span>
+                  </span>
+                </template>
+              </el-tree-select>
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.label" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="编码" prop="code" :rules="[{ required: true, message: '请输入编码', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.code" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="启用">
+              <el-switch v-model="form.enabled" />
+            </el-form-item>
+          </el-col> -->
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="说明">
+              <el-input v-model="form.description" clearable type="textarea" />
+            </el-form-item>
+          </el-col> -->
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/permission/permission-dot-form">
+import { reactive, toRefs, getCurrentInstance, ref, PropType } from 'vue'
+import { PermissionListOutput, PermissionUpdateDotInput, ApiListOutput,oauthPermissionDto,OauthPermission } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { ApiApi } from '/@/api/admin/Api'
+import { listToTree, treeToList } from '/@/utils/tree'
+import eventBus from '/@/utils/mitt'
+import { trimStart, replace, cloneDeep } from 'lodash-es'
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  permissionTreeData: {
+    type: Array as PropType<OauthPermission[]>,
+    default: () => [],
+  },
+})
+
+const { proxy } = getCurrentInstance() as any
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {} as oauthPermissionDto,
+  apiTreeData: [] as OauthPermission[],
+  expandRowKeys: [] as number[],
+  app:''
+})
+
+const { form } = toRefs(state)
+
+const getApis = async () => {
+  const res = await new PermissionApi().getPermissionInfo({appid:0})
+  res.data=res.data.map((item)=>{
+    if(item.parentId===''||item.parentId===null){
+      item.parentId=Number('0')
+      item.type=1
+    }
+    item.parentId=Number(item.parentId)
+    return item
+  })
+  if (res?.success && res.data && res.data.length > 0) {
+    state.apiTreeData = listToTree(res.data) as OauthPermission[]
+  } else {
+    state.apiTreeData = []
+  }
+}
+
+// 打开对话框
+const open = async (app:any,row: any = {}) => {
+  state.app=app
+  proxy.$modal.loading()
+
+  await getApis()
+
+  state.expandRowKeys = treeToList(cloneDeep(state.apiTreeData))
+    .filter((a: OauthPermission) => Number(a.parentId) === 0)
+    .map((a: OauthPermission) => a.id) as number[]
+
+  // if (row.id > 0) {
+  //   const res = await new PermissionApi().getPermissionInfo({appid:0,id: row.id}).catch(() => {
+  //     proxy.$modal.closeLoading()
+  //   })
+
+  //   if (res?.success) {
+  //     let formData = res.data as oauthPermissionDto
+  //     formData.parentId = formData.parentId && Number(formData.parentId) > 0 ? formData.parentId : undefined
+  //     state.form = formData
+  //   }
+  // } else {
+  //   state.form = {} as oauthPermissionDto
+  // }
+  state.form=row
+  proxy.$modal.closeLoading()
+  state.showDialog = true
+}
+
+const onApiFilterNode = (value: string, data: ApiListOutput) => {
+  if (!value) return true
+  return data.label?.indexOf(value) !== -1 || data.path?.indexOf(value) !== -1
+}
+
+const onApiCurrentChange = (data: ApiListOutput) => {
+  if (data) {
+    if (!state.form.label) {
+      state.form.label = data.label
+    }
+    if (!state.form.code) {
+      state.form.code = trimStart(replace(data.path || '', /\//g, ':'), ':')
+    }
+  }
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.sureLoading = true
+    let res = {} as any
+    state.form.parentId = state.form.parentId && Number(state.form.parentId) > 0 ? state.form.parentId : undefined
+    state.form.type=3
+    const appData=await new PermissionApi().getApplyInfo({name:state.app})
+    state.form.appID=appData.data[0].id
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshPermission')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>
+
+<style scoped lang="scss">
+.my-mlr-12 {
+  margin-left: 12px;
+  margin-right: 12px;
+}
+</style>

+ 233 - 0
admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-group-form.vue

@@ -0,0 +1,233 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用" prop="label">
+              <el-input v-model="state.app" clearable disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="上级分组">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="permissionTreeData"
+                node-key="id"
+                check-strictly
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                class="w100"
+              />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图">
+              <el-tree-select
+                v-model="form.viewId"
+                :data="state.viewTreeData"
+                node-key="id"
+                :props="{ label: 'path' }"
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                filterable
+                :filter-node-method="onViewFilterNode"
+                class="w100"
+              >
+                <template #default="{ data }">
+                  <span class="my-flex my-flex-between">
+                    <span>{{ data.label }}</span>
+                    <span class="my-line-1 my-ml-12" :title="data.path">
+                      {{ data.path }}
+                    </span>
+                  </span>
+                </template>
+              </el-tree-select>
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.label" clearable />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="路由地址" prop="path" :rules="[{ required: true, message: '请输入路由地址', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.path" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="路由命名">
+              <el-input v-model="form.name" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="重定向">
+              <el-input v-model="form.redirect" clearable placeholder="重定向地址" />
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="图标" prop="icon">
+              <my-select-icon v-model="form.icon" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="启用">
+              <el-switch v-model="form.enabled" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="展开">
+              <el-switch v-model="form.opened" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="隐藏">
+              <el-switch v-model="form.hidden" />
+            </el-form-item>
+          </el-col> -->
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/permission/permission-group-form">
+import { reactive, toRefs, getCurrentInstance, ref, PropType, defineAsyncComponent } from 'vue'
+import { PermissionListOutput, PermissionUpdateGroupInput, ViewListOutput,OauthPermission,oauthPermissionDto } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { ViewApi } from '/@/api/admin/View'
+import { listToTree } from '/@/utils/tree'
+import eventBus from '/@/utils/mitt'
+
+// 引入组件
+const MySelectIcon = defineAsyncComponent(() => import('/@/components/my-select-icon/index.vue'))
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  permissionTreeData: {
+    type: Array as PropType<OauthPermission[]>,
+    default: () => [],
+  },
+})
+const { proxy } = getCurrentInstance() as any
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {} as oauthPermissionDto,
+  viewTreeData: [] as OauthPermission[],
+  app:''
+})
+const { form } = toRefs(state)
+
+const getViews = async () => {
+  const res = await new PermissionApi().getPermissionInfo({appid:0})
+  res.data=res.data.map((item)=>{
+    if(item.parentId===''||item.parentId===null){
+      item.parentId=Number('0')
+      item.type=1
+    }
+    item.parentId=Number(item.parentId)
+    return item
+  })
+  if (res?.success && res.data && res.data.length > 0) {
+    state.viewTreeData = listToTree(res.data) as OauthPermission[]
+  } else {
+    state.viewTreeData = []
+  }
+}
+
+// 打开对话框
+const open = async (app:any,row: any = {}) => {
+  state.app=app
+  proxy.$modal.loading()
+  await getViews()
+  // if (row.id > 0) {
+  //   const res = await new PermissionApi().getPermissionInfo({appid:0,id: row.id}).catch(() => {
+  //     proxy.$modal.closeLoading()
+  //   })
+
+  //   if (res?.success) {
+  //     let formData = res.data as oauthPermissionDto
+  //     formData.parentId = formData.parentId && Number(formData.parentId) > 0 ? formData.parentId : undefined
+  //     state.form = formData
+  //   }
+  // } else {
+  //   state.form = { icon: 'ele-Memo' } as oauthPermissionDto
+  // }
+  state.form=row
+  proxy.$modal.closeLoading()
+  state.showDialog = true
+}
+
+// const onViewFilterNode = (value: string, data: OauthPermission) => {
+//   if (!value) return true
+//   return data.label?.indexOf(value) !== -1 || data.path?.indexOf(value) !== -1
+// }
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.sureLoading = true
+    let res = {} as any
+    state.form.parentId = state.form.parentId && Number(state.form.parentId) > 0 ? state.form.parentId : undefined
+    state.form.type=1
+    const appData=await new PermissionApi().getApplyInfo({name:state.app})
+    state.form.appID=appData.data[0].id
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshPermission')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 276 - 0
admin.ui.plus-master/src/views/admin/application/permissionManage/components/permission-menu-form.vue

@@ -0,0 +1,276 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用" prop="label">
+              <el-input v-model="state.app" clearable disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="上级分组">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="permissionTreeData"
+                node-key="id"
+                check-strictly
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                class="w100"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图">
+              <el-tree-select
+                v-model="form.viewId"
+                :data="state.viewTreeData"
+                node-key="id"
+                :props="{ label: 'path' }"
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                filterable
+                :filter-node-method="onViewFilterNode"
+                class="w100"
+                @current-change="onViewCurrentChange"
+              >
+                <template #default="{ data }">
+                  <span class="my-flex my-flex-between">
+                    <span>{{ data.label }}</span>
+                    <span class="my-line-1 my-ml-12" :title="data.path">
+                      {{ data.path }}
+                    </span>
+                  </span>
+                </template>
+              </el-tree-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.label" clearable />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="路由地址" prop="path" :rules="[{ required: true, message: '请输入路由地址', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.path" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="路由命名">
+              <el-input v-model="form.name" clearable />
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="图标" prop="icon">
+              <my-select-icon v-model="form.icon" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="启用">
+              <el-switch v-model="form.enabled" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="链接地址">
+              <el-input v-model="form.link" clearable placeholder="内嵌/外链链接地址" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="内嵌窗口">
+              <el-switch v-model="form.isIframe" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="缓存">
+              <el-switch v-model="form.isKeepAlive" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="固定">
+              <el-switch v-model="form.isAffix" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="隐藏">
+              <el-switch v-model="form.hidden" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="新窗口">
+              <el-switch v-model="form.newWindow" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="链接外显">
+              <el-switch v-model="form.external" />
+            </el-form-item>
+          </el-col> -->
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/permission/permission-menu-form">
+import { reactive, toRefs, getCurrentInstance, ref, PropType, defineAsyncComponent } from 'vue'
+import { OauthView,OauthPermission,oauthPermissionDto } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { ViewApi } from '/@/api/admin/View'
+import { listToTree } from '/@/utils/tree'
+import eventBus from '/@/utils/mitt'
+
+// 引入组件
+const MySelectIcon = defineAsyncComponent(() => import('/@/components/my-select-icon/index.vue'))
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  permissionTreeData: {
+    type: Array as PropType<OauthPermission[]>,
+    default: () => [],
+  },
+})
+
+const { proxy } = getCurrentInstance() as any
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {} as oauthPermissionDto,
+  viewTreeData: [] as OauthView[],
+  app:''
+})
+const { form } = toRefs(state)
+
+const getViews = async (app) => {
+  const appDate=await new PermissionApi().getApplyInfo({Name:app})
+  const res = await new PermissionApi().getViewInfo({appid:appDate.data[0].id})
+  res.data=res.data.map((item)=>{
+    if(item.parentId===''||item.parentId===null){
+      item.parentId=Number('0')
+      item.type=1
+    }
+    item.parentId=Number(item.parentId)
+    return item
+  })
+  if (res?.success && res.data && res.data.length > 0) {
+    state.viewTreeData = listToTree(res.data) as OauthView[]
+  } else {
+    state.viewTreeData = []
+  }
+}
+
+// 打开对话框
+const open = async (app:any,row: any = {}) => {
+  state.app=app
+  proxy.$modal.loading()
+
+  await getViews(app)
+
+  // if (row.id > 0) {
+  //   const res = await new PermissionApi().getPermissionInfo({appid:0,id: row.id}).catch(() => {
+  //     proxy.$modal.closeLoading()
+  //   })
+
+  //   if (res?.success) {
+  //     let formData = res.data as oauthPermissionDto
+  //     formData.parentId = formData.parentId && Number(formData.parentId) > 0 ? formData.parentId : undefined
+  //     state.form = formData
+  //   }
+  // } else {
+  //   state.form = { icon: 'ele-Memo' } as oauthPermissionDto
+  // }
+  state.form=row
+  proxy.$modal.closeLoading()
+  state.showDialog = true
+}
+
+const onViewFilterNode = (value: string, data: OauthView) => {
+  if (!value) return true
+  return data.label?.indexOf(value) !== -1 || data.path?.indexOf(value) !== -1
+}
+
+const onViewCurrentChange = (data: OauthView) => {
+  if (data) {
+    if (!state.form.label) {
+      state.form.label = data.label
+    }
+    if (!state.form.viewPath) {
+      state.form.viewPath = '/' + data.path
+    }
+  }
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.sureLoading = true
+    let res = {} as any
+    state.form.parentId = state.form.parentId && Number(state.form.parentId) > 0 ? state.form.parentId : undefined
+    state.form.type=2
+    const appData=await new PermissionApi().getApplyInfo({name:state.app})
+    state.form.appID=appData.data[0].id
+    console.log(state.form)
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addAppPermission(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshPermission')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>
+
+<style scoped lang="scss">
+.my-ml-12 {
+  margin-left: 12px;
+}
+</style>

+ 242 - 0
admin.ui.plus-master/src/views/admin/application/permissionManage/index.vue

@@ -0,0 +1,242 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :model="state.filter" :inline="true" @submit.stop.prevent>
+        <el-form-item label="应用">
+          <el-input v-model="state.filter.app" disabled placeholder="请选择应用" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item label="权限名称">
+          <el-input v-model="state.filter.name" placeholder="权限名称" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+          <el-dropdown
+            v-auths="['api:admin:permission:addgroup', 'api:admin:permission:addmenu', 'api:admin:permission:adddot']"
+            style="margin-left: 12px"
+          >
+            <el-button type="primary"
+              >新增<el-icon class="el-icon--right"><ele-ArrowDown /></el-icon
+            ></el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item v-if="auth('api:admin:permission:addgroup')" @click="onAdd(1)">新增分组</el-dropdown-item>
+                <el-dropdown-item v-if="auth('api:admin:permission:addmenu')" @click="onAdd(2)">新增菜单</el-dropdown-item>
+                <el-dropdown-item v-if="auth('api:admin:permission:adddot')" @click="onAdd(3)">新增权限点</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table
+        :data="state.permissionTreeData"
+        style="width: 100%"
+        v-loading="state.loading"
+        row-key="id"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+        :expand-row-keys="state.expandRowKeys"
+      >
+        <el-table-column prop="label" label="权限名称" width="240" show-overflow-tooltip>
+          <template #default="{ row }">
+            <SvgIcon :name="row.icon" />
+            {{ row.label }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="type" label="类型" width="80" show-overflow-tooltip>
+          <template #default="{ row }">
+            {{ row.type === 1 ? '分组' : row.type === 2 ? '菜单' : row.type === 3 ? '权限点' : '' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="path" label="权限地址" min-width="240" show-overflow-tooltip>
+          <template #default="{ row }">
+            <div v-if="row.type === 1 || row.type === 2">
+              {{ row.viewPath ? '视图地址:' + row.viewPath : '' }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sort" label="排序" width="80" align="center" show-overflow-tooltip />
+        <!-- <el-table-column label="状态" width="80" align="center" show-overflow-tooltip>
+          <template #default="{ row }">
+            <el-tag type="success" v-if="row.enabled">启用</el-tag>
+            <el-tag type="danger" v-else>禁用</el-tag>
+          </template>
+        </el-table-column> -->
+        <el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
+          <template #default="{ row }">
+            <el-button
+              v-if="
+                (row.type === 1 && auth('api:admin:permission:updategroup')) ||
+                (row.type === 2 && auth('api:admin:permission:updatemenu')) ||
+                (row.type === 3 && auth('api:admin:permission:updatedot'))
+              "
+              icon="ele-EditPen"
+              text
+              type="primary"
+              @click="onEdit(row)"
+              >编辑</el-button
+            >
+            <el-button v-auth="'api:admin:permission:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <permission-group-form
+      ref="permissionGroupFormRef"
+      :title="state.permissionFormTitle"
+      :permission-tree-data="state.formPermissionGroupTreeData"
+    ></permission-group-form>
+
+    <permission-menu-form
+      ref="permissionMenuFormRef"
+      :title="state.permissionFormTitle"
+      :permission-tree-data="state.formPermissionGroupTreeData"
+    ></permission-menu-form>
+
+    <permission-dot-form
+      ref="permissionDotFormRef"
+      :title="state.permissionFormTitle"
+      :permission-tree-data="state.formPermissionMenuTreeData"
+    ></permission-dot-form>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/permission">
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
+import { OauthPermission,OauthApply } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { listToTree, treeToList, filterTree } from '/@/utils/tree'
+import router from "/@/router";
+import { cloneDeep } from 'lodash-es'
+import eventBus from '/@/utils/mitt'
+import { auth } from '/@/utils/authFunction'
+import { it } from 'node:test'
+
+// 引入组件
+const PermissionGroupForm = defineAsyncComponent(() => import('./components/permission-group-form.vue'))
+const PermissionMenuForm = defineAsyncComponent(() => import('./components/permission-menu-form.vue'))
+const PermissionDotForm = defineAsyncComponent(() => import('./components/permission-dot-form.vue'))
+
+const { proxy } = getCurrentInstance() as any
+
+const permissionGroupFormRef = ref()
+const permissionMenuFormRef = ref()
+const permissionDotFormRef = ref()
+const state = reactive({
+  loading: false,
+  permissionFormTitle: '',
+  filter: {
+    name: '',
+    app:''
+  },
+  permissionTreeData: [] as Array<OauthPermission>,
+  formPermissionGroupTreeData: [] as Array<OauthPermission>,
+  formPermissionMenuTreeData: [] as Array<OauthPermission>,
+  expandRowKeys: [] as string[]
+})
+
+onMounted(async () => {
+  await onQuery()
+  state.expandRowKeys = treeToList(cloneDeep(state.permissionTreeData))
+    .filter((a: OauthPermission) => a.opened === true)
+    .map((a: OauthPermission) => a.id + '') as string[]
+  eventBus.off('refreshPermission')
+  eventBus.on('refreshPermission', async () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshPermission')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  const name = router.currentRoute.value.params.id
+  const appData=await new PermissionApi().getApplyInfo({Name:name})
+  state.filter.app=appData.data[0].name
+  const res = await new PermissionApi().getPermissionInfo({appid:appData.data[0].id,label:state.filter.name}).catch(() => {
+    state.loading = false
+  })
+  res.data=res.data.map((item)=>{
+    item.parentId=Number(item.parentId)
+    if(item.parentId===0||item.parentId===null){
+      //item.parentId=Number('0')
+      item.type=1
+    }
+    // if(item.parentId===0){
+    //   //item.parentId=Number(item.parentId)
+    //   item.type=2
+    // }
+    // if(item.parentId!==0){
+    //   //item.parentId=Number(item.parentId)
+    //   item.type=3
+    // }
+    return item
+  })
+  if (res && res.data && res.data.length > 0) {
+    state.permissionTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.name, {
+      filterWhere: (item: any, keyword: string) => {
+        return item.label?.toLocaleLowerCase().indexOf(keyword) > -1
+      },
+    })
+    state.formPermissionGroupTreeData = listToTree(cloneDeep(res.data).filter((a) => a.type === 1))
+    state.formPermissionMenuTreeData = listToTree(cloneDeep(res.data).filter((a) => a.type === 1 || a.type === 2))
+  } else {
+    state.permissionTreeData = []
+    state.formPermissionGroupTreeData = []
+    state.formPermissionMenuTreeData = []
+  }
+  state.loading = false
+}
+
+const onAdd = (type: number) => {
+  switch (type) {
+    case 1:
+      state.permissionFormTitle = '新增分组'
+      permissionGroupFormRef.value.open(state.filter.app)
+      break
+    case 2:
+      state.permissionFormTitle = '新增菜单'
+      permissionMenuFormRef.value.open(state.filter.app)
+      break
+    case 3:
+      state.permissionFormTitle = '新增权限点'
+      permissionDotFormRef.value.open(state.filter.app)
+      break
+  }
+}
+
+const onEdit = (row: OauthPermission) => {
+  switch (row.type) {
+    case 1:
+      state.permissionFormTitle = '编辑分组'
+      permissionGroupFormRef.value.open(state.filter.app,row)
+      break
+    case 2:
+      state.permissionFormTitle = '编辑菜单'
+      permissionMenuFormRef.value.open(state.filter.app,row)
+      break
+    case 3:
+      state.permissionFormTitle = '编辑权限点'
+      permissionDotFormRef.value.open(state.filter.app,row)
+      break
+  }
+}
+
+const onDelete = (row: OauthPermission) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除${row.type === 1 ? '分组' : row.type === 2 ? '菜单' : row.type === 3 ? '权限点' : ''}【${row.label}】?`)
+    .then(async () => {
+      await new PermissionApi().permissionDelete({id:row.id}, { loading: true })
+      onQuery()
+    })
+    .catch(() => {})
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 110 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/components/org-menu.vue

@@ -0,0 +1,110 @@
+<template>
+  <el-card shadow="never" style="margin-top: 8px" body-style="padding:0px;" class="my-fill">
+    <template #header>
+      <el-input v-model="state.filterText" placeholder="筛选部门" clearable />
+    </template>
+    <el-scrollbar v-loading="state.loading" height="100%" max-height="100%" :always="false" wrap-style="padding:var(--el-card-padding)">
+      <el-tree
+        ref="orgMenuRef"
+        :data="state.orgTreeData"
+        node-key="id"
+        :props="{ children: 'children', label: 'name' }"
+        :filter-node-method="onFilterNode"
+        highlight-current
+        check-strictly
+        default-expand-all
+        render-after-expand
+        :expand-on-click-node="false"
+        v-bind="$attrs"
+        @node-click="onNodeClick"
+        @check-change="onCheckChange"
+      />
+    </el-scrollbar>
+  </el-card>
+</template>
+
+<script lang="ts" setup name="admin/org/menu">
+import { onMounted, reactive, ref, watch, nextTick } from 'vue'
+import { OrgListOutput } from '/@/api/admin/data-contracts'
+import { OrgApi } from '/@/api/admin/Org'
+import { listToTree } from '/@/utils/tree'
+import { ElTree } from 'element-plus'
+
+interface Props {
+  modelValue: number[] | null | undefined
+  selectFirstNode: boolean
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => [],
+  selectFirstNode: false,
+})
+
+const orgMenuRef = ref<InstanceType<typeof ElTree>>()
+const state = reactive({
+  loading: false,
+  filterText: '',
+  orgTreeData: [] as Array<OrgListOutput>,
+  lastKey: 0,
+})
+
+watch(
+  () => state.filterText,
+  (val) => {
+    orgMenuRef.value?.filter(val)
+  }
+)
+
+onMounted(() => {
+  initData()
+})
+
+const emits = defineEmits<{
+  (e: 'node-click', node: OrgListOutput | null): void
+  (e: 'update:modelValue', node: any[] | undefined | null): void
+}>()
+
+const onFilterNode = (value: string, data: OrgListOutput) => {
+  if (!value) return true
+  return data.name?.indexOf(value) !== -1
+}
+
+const onNodeClick = (node: OrgListOutput) => {
+  if (state.lastKey === node.id) {
+    state.lastKey = 0
+    orgMenuRef.value?.setCurrentKey(undefined)
+    emits('node-click', null)
+  } else {
+    state.lastKey = node.id as number
+    emits('node-click', node)
+  }
+}
+
+const onCheckChange = () => {
+  emits('update:modelValue', orgMenuRef.value?.getCheckedKeys())
+}
+
+const initData = async () => {
+  state.loading = true
+  const res = await new OrgApi().getList().catch(() => {
+    state.loading = false
+  })
+  state.loading = false
+  if (res?.success && res.data && res.data.length > 0) {
+    state.orgTreeData = listToTree(res.data)
+    if (state.orgTreeData.length > 0 && props.selectFirstNode) {
+      nextTick(() => {
+        const firstNode = state.orgTreeData[0]
+        orgMenuRef.value?.setCurrentKey(firstNode.id)
+        emits('node-click', firstNode)
+      })
+    }
+  } else {
+    state.orgTreeData = []
+  }
+}
+
+defineExpose({
+  orgMenuRef,
+})
+</script>

+ 141 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/components/role-form.vue

@@ -0,0 +1,141 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="上级分组" prop="parentId" :rules="[{ required: true, message: '请选择上级分组', trigger: ['change'] }]">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="roleTreeData"
+                node-key="id"
+                :props="{ label: 'name' }"
+                check-strictly
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                class="w100"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.name" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="编码" prop="code">
+              <el-input v-model="form.code" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+          <el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="说明">
+              <el-input v-model="form.description" clearable type="textarea" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/role/form">
+import { reactive, toRefs, ref, PropType } from 'vue'
+import { RoleGetListOutput, RoleUpdateInput,OauthRole } from '/@/api/admin/data-contracts'
+import { RoleApi } from '/@/api/admin/Role'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { cloneDeep } from 'lodash-es'
+import eventBus from '/@/utils/mitt'
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  roleTreeData: {
+    type: Array as PropType<OauthRole[]>,
+    default: () => [],
+  },
+})
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {} as OauthRole,
+})
+
+const { form } = toRefs(state)
+
+// 打开对话框
+const open = async (row: OauthRole) => {
+  let formData = cloneDeep(row) as OauthRole
+  // if (row.id > 0) {
+  //   const res = await new RoleApi().get({ id: row.id }, { loading: true })
+
+  //   if (res?.success) {
+  //     formData = res.data as OauthRole
+  //     formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
+  //   }
+  // }
+
+  state.form = formData
+  state.showDialog = true
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+    debugger
+    state.sureLoading = true
+    let res = {} as any
+    state.form.parentId = state.form.parentId && Number(state.form.parentId) > 0 ? state.form.parentId : undefined
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addRole(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addRole(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshRole')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 152 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/components/set-app-menu.vue

@@ -0,0 +1,152 @@
+<template>
+  <div>
+    <el-dialog
+    v-model="state.showDialog"
+    destroy-on-close
+    :title="innerTitle"
+    append-to-body
+    draggable
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="780px"
+  >
+    <div>
+      <el-radio-group v-model="selectedApp">
+        <el-radio v-for="(item,index) in state.permissionTreeData" :key="index" :label="item.id">{{ item.name }}</el-radio>
+      </el-radio-group>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="onCancel" size="default">取 消</el-button>
+        <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+  <set-role-menu ref="setRoleMenuRef"></set-role-menu>
+  </div>
+
+</template>
+
+<script lang="ts" setup name="admin/role/components/set-role-menu">
+import { ref, reactive, getCurrentInstance, computed,defineAsyncComponent } from 'vue'
+import { RoleGetListOutput, PermissionAssignInput,OauthApply } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { ElMessage, ElTree } from 'element-plus'
+import { listToTree } from '/@/utils/tree'
+import { cloneDeep } from 'lodash-es'
+const SetRoleMenu = defineAsyncComponent(() => import('../components/set-role-menu.vue'))
+const props = defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+})
+const setRoleMenuRef = ref()
+const innerTitle = computed(() => {
+  return props.title ? props.title : state.roleName ? `选择【${state.roleName}】应用权限` : '设置应用权限'
+})
+
+const state = reactive({
+  showDialog: false,
+  loading: false,
+  sureLoading: false,
+  permissionTreeData: [] as Array<OauthApply>,
+  roleId: 0 as number | undefined,
+  roleName: '' as string | undefined | null,
+  checkedKeys: [],
+})
+const selectedApp=ref('')
+
+const { proxy } = getCurrentInstance() as any
+
+// 打开对话框
+const open = async (role: RoleGetListOutput) => {
+  state.roleId = role.id
+  state.roleName = role.name
+  proxy.$modal.loading()
+  await onQuery()
+  //await getAppPermissionList()
+  proxy.$modal.closeLoading()
+  state.showDialog = true
+}
+
+// 关闭对话框
+const close = () => {
+  state.showDialog = false
+}
+
+const onQuery = async () => {
+  state.loading = true
+
+  const res = await new PermissionApi().getApplyInfo({ id:state.roleId }).catch(() => {
+    state.loading = false
+  })
+  if (res && res.data && res.data.length > 0) {
+    state.permissionTreeData = res.data
+  } else {
+    state.permissionTreeData = []
+  }
+  state.loading = false
+}
+
+const customNodeClass = (data: any) => {
+  return data.row ? 'is-penultimate' : ''
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = async () => {
+  state.sureLoading = true
+  if(selectedApp.value===''){
+    ElMessage.error('请选择应用')
+    state.sureLoading = false
+  }else{
+    const input = { id: state.roleId, name:state.roleName }
+    // const res = await new PermissionApi().roleMenu(input, { showSuccessMessage: true }).catch(() => {
+    //   state.sureLoading = false
+    // })
+
+    state.sureLoading = false
+    state.showDialog = false
+    // if (res?.success) {
+    //   state.showDialog = false
+    // }
+    setRoleMenuRef.value.open(input,selectedApp.value)
+  }
+}
+
+defineExpose({
+  open,
+  close,
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.el-dialog__body) {
+  padding: 5px 10px;
+}
+:deep(.is-penultimate) {
+  .el-tree-node__children {
+    padding-left: 65px;
+    white-space: pre-wrap;
+    line-height: 100%;
+
+    .el-tree-node {
+      display: inline-block;
+    }
+
+    .el-tree-node__content {
+      padding-left: 12px !important;
+      padding-right: 12px;
+
+      .el-tree-node__expand-icon.is-leaf {
+        display: none;
+      }
+    }
+  }
+}
+</style>

+ 163 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/components/set-role-menu.vue

@@ -0,0 +1,163 @@
+<template>
+  <el-dialog
+    v-model="state.showDialog"
+    destroy-on-close
+    :title="innerTitle"
+    append-to-body
+    draggable
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="780px"
+  >
+    <div>
+      <el-tree
+        ref="permissionTreeRef"
+        :data="state.permissionTreeData"
+        node-key="id"
+        show-checkbox
+        highlight-current
+        default-expand-all
+        check-on-click-node
+        :expand-on-click-node="false"
+        :props="{ class: customNodeClass }"
+        :default-checked-keys="state.checkedKeys"
+      />
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="onCancel" size="default">取 消</el-button>
+        <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup name="admin/role/components/set-role-menu">
+import { ref, reactive, getCurrentInstance, computed } from 'vue'
+import { RoleGetListOutput, PermissionAssignInput } from '/@/api/admin/data-contracts'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { ElTree } from 'element-plus'
+import { listToTree } from '/@/utils/tree'
+import { cloneDeep } from 'lodash-es'
+
+const props = defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+})
+
+const innerTitle = computed(() => {
+  return props.title ? props.title : state.roleName ? `设置【${state.roleName}】菜单权限` : '设置菜单权限'
+})
+
+const state = reactive({
+  showDialog: false,
+  loading: false,
+  sureLoading: false,
+  permissionTreeData: [],
+  roleId: 0 as number | undefined,
+  roleName: '' as string | undefined | null,
+  checkedKeys: [],
+  appid:0
+})
+
+const { proxy } = getCurrentInstance() as any
+const permissionTreeRef = ref<InstanceType<typeof ElTree>>()
+
+const getRolePermissionList = async () => {
+  const res = await new PermissionApi().getMenuInfo( {roleid:state.roleId,applyid:state.appid} )
+  state.checkedKeys = res?.success ? (res.data as never[]) : []
+}
+
+// 打开对话框
+const open = async (role: RoleGetListOutput,appId:any) => {
+  state.roleId = role.id
+  state.roleName = role.name
+  state.appid=appId
+  proxy.$modal.loading()
+  await onQuery()
+  await getRolePermissionList()
+  proxy.$modal.closeLoading()
+  state.showDialog = true
+}
+
+// 关闭对话框
+const close = () => {
+  state.showDialog = false
+}
+
+const onQuery = async () => {
+  state.loading = true
+
+  const res = await new PermissionApi().getPermissionInfo({appid:state.appid}).catch(() => {
+    state.loading = false
+  })
+  res.data=res.data.map((item)=>{
+    item.parentId=Number(item.parentId)
+    return item
+  })
+  if (res && res.data && res.data.length > 0) {
+    state.permissionTreeData = listToTree(cloneDeep(res.data))
+  } else {
+    state.permissionTreeData = []
+  }
+
+  state.loading = false
+}
+
+const customNodeClass = (data: any) => {
+  return data.row ? 'is-penultimate' : ''
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = async () => {
+  state.sureLoading = true
+  const permissionIds = permissionTreeRef.value?.getCheckedKeys(true)
+  const input = { strings: permissionIds }
+  const res = await new PermissionApi().roleMenuAssign(input,{applyid:String(state.appid),roleId: String(state.roleId)} ,{ showSuccessMessage: true }).catch(() => {
+    state.sureLoading = false
+  })
+  state.sureLoading = false
+
+  if (res?.success) {
+    state.showDialog = false
+  }
+}
+
+defineExpose({
+  open,
+  close,
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.el-dialog__body) {
+  padding: 5px 10px;
+}
+:deep(.is-penultimate) {
+  .el-tree-node__children {
+    padding-left: 65px;
+    white-space: pre-wrap;
+    line-height: 100%;
+
+    .el-tree-node {
+      display: inline-block;
+    }
+
+    .el-tree-node__content {
+      padding-left: 12px !important;
+      padding-right: 12px;
+
+      .el-tree-node__expand-icon.is-leaf {
+        display: none;
+      }
+    }
+  }
+}
+</style>

+ 202 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/components/user-select.vue

@@ -0,0 +1,202 @@
+<template>
+  <el-dialog
+    v-model="state.showDialog"
+    destroy-on-close
+    :title="title"
+    append-to-body
+    draggable
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="780px"
+  >
+    <div style="padding: 0px 0px 8px 8px; background-color: var(--ba-bg-color)">
+      <el-row :gutter="8" style="width: 100%">
+        <el-col :xs="24" :sm="9">
+          <div class="my-flex-column h100">
+            <org-menu @node-click="onOrgNodeClick" class="my-flex-fill"></org-menu>
+          </div>
+        </el-col>
+        <el-col :xs="24" :sm="15">
+          <el-card shadow="never" :body-style="{ paddingBottom: '0' }" style="margin-top: 8px">
+            <el-form :model="state.filter" :inline="true" @submit.stop.prevent>
+              <el-form-item label="姓名" prop="name">
+                <el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onQuery" />
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+              </el-form-item>
+            </el-form>
+          </el-card>
+
+          <el-card shadow="never" style="margin-top: 8px">
+            <el-table
+              ref="userTableRef"
+              :data="state.userListData"
+              style="width: 100%"
+              v-loading="state.loading"
+              row-key="id"
+              default-expand-all
+              :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+              :highlight-current-row="!multiple"
+              @row-click="onRowClick"
+              @row-dblclick="onRowDbClick"
+            >
+              <el-table-column v-if="multiple" type="selection" width="55" />
+              <el-table-column prop="name" label="姓名" min-width="80" show-overflow-tooltip />
+              <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
+              <!-- <el-table-column prop="email" label="邮箱" min-width="120" show-overflow-tooltip /> -->
+            </el-table>
+            <div class="my-flex my-flex-end" style="margin-top: 20px">
+              <el-pagination
+                v-model:currentPage="state.pageInput.currentPage"
+                v-model:page-size="state.pageInput.pageSize"
+                :total="state.total"
+                :page-sizes="[10, 20, 50, 100]"
+                small
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                layout="total, sizes, prev, pager, next"
+              />
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="onCancel" size="default">取 消</el-button>
+        <el-button type="primary" @click="onSure" size="default" :loading="sureLoading">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup name="example/application/roleManage/components/user-select">
+import { ref, reactive, defineAsyncComponent } from 'vue'
+import { ElTable } from 'element-plus'
+import { UserDto, PageInputUserGetPageDto, OrgListOutput } from '/@/api/admin/data-contracts'
+import { UserApi } from '/@/api/admin/User'
+
+// 引入组件
+const OrgMenu = defineAsyncComponent(() => import('./org-menu.vue'))
+
+const props = defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  multiple: {
+    type: Boolean,
+    default: false,
+  },
+  sureLoading: {
+    type: Boolean,
+    default: false,
+  },
+})
+
+const emits = defineEmits(['sure'])
+
+const userTableRef = ref<InstanceType<typeof ElTable>>()
+
+const state = reactive({
+  showDialog: false,
+  loading: false,
+  filter: {
+    name: '',
+  },
+  total: 0,
+  pageInput: {
+    currentPage: 1,
+    pageSize: 20,
+    filter: {
+      orgId: null,
+    },
+  } as PageInputUserGetPageDto,
+  userListData: [] as Array<UserDto>,
+})
+
+// 打开对话框
+const open = () => {
+  state.showDialog = true
+  if (state.pageInput.filter) {
+    state.pageInput.filter.orgId = null
+  }
+
+  onQuery()
+}
+
+// 关闭对话框
+const close = () => {
+  state.showDialog = false
+}
+
+const onQuery = async () => {
+  state.loading = true
+  state.pageInput.dynamicFilter = {
+    field: 'name',
+    operator: 0,
+    value: state.filter.name,
+  }
+  const res = await new UserApi().getPage(state.pageInput).catch(() => {
+    state.loading = false
+  })
+
+  state.userListData = res?.data?.list ?? []
+  state.total = res?.data?.total ?? 0
+  state.loading = false
+}
+
+const onSizeChange = (val: number) => {
+  state.pageInput.pageSize = val
+  onQuery()
+}
+
+const onCurrentChange = (val: number) => {
+  state.pageInput.currentPage = val
+  onQuery()
+}
+
+const onOrgNodeClick = (node: OrgListOutput | null) => {
+  if (state.pageInput.filter) {
+    state.pageInput.filter.orgId = node?.id
+  }
+  onQuery()
+}
+
+const onRowClick = (row:UserDto) => {
+  // TODO: improvement typing when refactor table
+  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+  // @ts-expect-error
+  userTableRef.value!.toggleRowSelection(row, props.multiple ? undefined : true)
+}
+
+const onRowDbClick = () => {
+  if (!props.multiple) {
+    onSure()
+  }
+}
+
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  const selectionRows = userTableRef.value!.getSelectionRows() as UserDto[]
+  emits('sure', props.multiple ? selectionRows : selectionRows.length > 0 ? selectionRows[0] : null)
+}
+
+defineExpose({
+  open,
+  close,
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.el-dialog__body) {
+  padding: 5px 10px;
+}
+</style>

+ 373 - 0
admin.ui.plus-master/src/views/admin/application/roleManage/index.vue

@@ -0,0 +1,373 @@
+<template>
+  <my-layout>
+    <pane size="50" min-size="30" max-size="70">
+      <div class="my-flex-column w100 h100">
+        <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+          <el-form :inline="true" @submit.stop.prevent>
+            <el-form-item label="应用">
+              <el-input v-model="state.filter.app" disabled placeholder="请选择应用" @keyup.enter="onQuery" />
+            </el-form-item>
+            <el-form-item label="角色名称">
+              <el-input v-model="state.filter.roleName" placeholder="角色名称" @keyup.enter="onQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+              <el-dropdown v-auth="'api:admin:role:add'" style="margin-left: 12px">
+                <el-button type="primary"
+                  >新增<el-icon class="el-icon--right"><ele-ArrowDown /></el-icon
+                ></el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item @click="onAdd(1)">新增分组</el-dropdown-item>
+                    <el-dropdown-item @click="onAdd(2)">新增角色</el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <el-card class="my-fill mt8" shadow="never">
+          <el-table
+            ref="roleTableRef"
+            v-loading="state.loading"
+            :data="state.roleTreeData"
+            row-key="id"
+            default-expand-all
+            :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+            highlight-current-row
+            style="width: 100%"
+            @current-change="onCurrentChange"
+          >
+            <el-table-column prop="name" label="角色名称" min-width="120" show-overflow-tooltip />
+            <el-table-column prop="sort" label="排序" width="80" align="center" show-overflow-tooltip />
+            <el-table-column label="操作" width="100" fixed="right" header-align="center" align="right">
+              <template #default="{ row }">
+                <el-button
+                  v-if="row.type === 1"
+                  v-auth="'api:admin:role:add'"
+                  icon="ele-Plus"
+                  text
+                  type="primary"
+                  @click="onAdd(2, row)"
+                ></el-button>
+                <my-dropdown-more icon-only v-auths="['api:admin:permission:assign', 'api:admin:role:update', 'api:admin:role:delete']">
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item v-if="row.type === 2 && auth('api:admin:permission:assign')" @click="onSetAppMenu(row)"
+                        >菜单权限</el-dropdown-item
+                      >
+                      <!-- <el-dropdown-item v-if="row.type === 2" @click="onSetRoleDataScope(row)">数据权限</el-dropdown-item> -->
+                      <el-dropdown-item v-if="auth('api:admin:role:update')" @click="onEdit(row)"
+                        >编辑{{ row.type === 1 ? '分组' : '角色' }}</el-dropdown-item
+                      >
+                      <el-dropdown-item v-if="auth('api:admin:role:delete')" @click="onDelete(row)"
+                        >删除{{ row.type === 1 ? '分组' : '角色' }}</el-dropdown-item
+                      >
+                    </el-dropdown-menu>
+                  </template>
+                </my-dropdown-more>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </div>
+    </pane>
+    <pane>
+      <div class="my-flex-column w100 h100">
+        <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+          <el-form :inline="true" @submit.stop.prevent>
+            <el-form-item label="姓名">
+              <el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetRoleUserList" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="ele-Search" @click="onGetRoleUserList"> 查询 </el-button>
+              <el-button v-auth="'api:admin:role:add-role-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加用户 </el-button>
+              <el-button v-auth="'api:admin:role:remove-role-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除用户 </el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <el-card class="my-fill mt8" shadow="never">
+          <el-table
+            ref="userTableRef"
+            v-loading="state.userListLoading"
+            :data="state.userListData"
+            row-key="id"
+            style="width: 100%"
+            @row-click="onUserRowClick"
+          >
+            <el-table-column type="selection" width="55" />
+            <el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
+            <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
+            <!-- <el-table-column prop="email" label="邮箱" min-width="120" show-overflow-tooltip /> -->
+          </el-table>
+        </el-card>
+      </div>
+    </pane>
+
+    <role-form ref="roleFormRef" :title="state.roleFormTitle" :role-tree-data="state.roleFormTreeData"></role-form>
+    <user-select
+      ref="userSelectRef"
+      :title="`添加【${state.roleName}】用户`"
+      multiple
+      :sure-loading="state.sureLoading"
+      @sure="onSureUser"
+    ></user-select>
+    <set-app-menu ref="setAppMenuRef"></set-app-menu>
+    <set-role-menu ref="setRoleMenuRef"></set-role-menu>
+    <set-role-data-scope ref="setRoleDataScopeRef"></set-role-data-scope>
+  </my-layout>
+</template>
+
+<script lang="ts" setup name="admin/role">
+import router from "/@/router";
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, nextTick, defineAsyncComponent } from 'vue'
+import { OauthRole,RoleGetListOutput, RoleType,UserDto } from '/@/api/admin/data-contracts'
+import { RoleApi } from '/@/api/admin/Role'
+import { PermissionApi } from '/@/api/admin/Permission'
+import { listToTree, filterTree } from '/@/utils/tree'
+import { ElTable } from 'element-plus'
+import { cloneDeep } from 'lodash-es'
+import eventBus from '/@/utils/mitt'
+import { auth } from '/@/utils/authFunction'
+import { Pane } from 'splitpanes'
+
+// 引入组件
+const RoleForm = defineAsyncComponent(() => import('./components/role-form.vue'))
+const SetAppMenu = defineAsyncComponent(() => import('./components/set-app-menu.vue'))
+const UserSelect = defineAsyncComponent(() => import('./components/user-select.vue'))
+const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
+const MyLayout = defineAsyncComponent(() => import('/@/components/my-layout/index.vue'))
+const SetRoleMenu = defineAsyncComponent(() => import('./components/set-role-menu.vue'))
+const { proxy } = getCurrentInstance() as any
+
+const roleTableRef = ref()
+const roleFormRef = ref()
+const userTableRef = ref<InstanceType<typeof ElTable>>()
+const userSelectRef = ref()
+const setAppMenuRef = ref()
+const setRoleMenuRef = ref()
+const setRoleDataScopeRef = ref()
+
+const state = reactive({
+  loading: false,
+  userListLoading: false,
+  sureLoading: false,
+  roleFormTitle: '',
+  filter: {
+    name: '',
+    roleName: '',
+    app:''
+  },
+  roleTreeData: [] as any,
+  roleFormTreeData: [] as any,
+  userListData: [] as UserDto[],
+  roleId: undefined as number | undefined,
+  roleName: '' as string | null | undefined,
+  appid:undefined as number | undefined
+})
+
+onMounted(() => {
+  onQuery()
+  eventBus.off('refreshRole')
+  eventBus.on('refreshRole', async () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshRole')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  const name = router.currentRoute.value.params.id
+  const appData=await new PermissionApi().getApplyInfo({Name:name})
+  state.filter.app=appData.data[0].name
+  debugger
+  state.appid = appData.data[0].id
+  const res = await new PermissionApi().getRoleInfo({name:state.filter.roleName,AppID:appData.data[0].id}).catch(() => {
+    state.loading = false
+  })
+  res.data=res.data.map((item)=>{
+    item.parentId=Number(item.parentId)
+    if(item.parentId===0||item.parentId===null){
+      //item.parentId=Number('0')
+      item.type=1
+    }
+    // if(item.parentId===0){
+    //   //item.parentId=Number(item.parentId)
+    //   item.type=2
+    // }
+    // if(item.parentId!==0){
+    //   //item.parentId=Number(item.parentId)
+    //   item.type=3
+    // }
+    return item
+  })
+  if (res && res.data && res.data.length > 0) {
+    state.roleTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.roleName)
+    state.roleFormTreeData = listToTree(cloneDeep(res.data).filter((a) => a.parentId === 0))
+    if (state.roleTreeData.length > 0 && state.roleTreeData[0].children?.length > 0) {
+      nextTick(() => {
+        roleTableRef.value!?.setCurrentRow(state.roleTreeData[0].children[0])
+      })
+    }
+  } else {
+    state.roleTreeData = []
+    state.roleFormTreeData = []
+  }
+
+  state.loading = false
+}
+
+const onAdd = (type: RoleType, row: OauthRole | undefined = undefined) => {
+  switch (type) {
+    case 1:
+      debugger
+      state.roleFormTitle = '新增分组'
+      roleFormRef.value.open({ type: 1 ,appid:state.appid})
+      break
+    case 2:
+      state.roleFormTitle = '新增角色'
+      roleFormRef.value.open({ type: 2, parentId: row?.id, dataScope: 1,appid:state.appid })
+      break
+  }
+}
+
+const onEdit = (row: OauthRole) => {
+  switch (row.type) {
+    case 1:
+      state.roleFormTitle = '编辑分组'
+      break
+    case 2:
+      state.roleFormTitle = '编辑角色'
+      break
+  }
+  roleFormRef.value.open(row)
+}
+
+const onDelete = (row: OauthRole) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除角色【${row.name}】?`)
+    .then(async () => {
+      await new PermissionApi().roleDelete({ id: row.id }, { loading: true })
+      onQuery()
+    })
+    .catch(() => {})
+}
+
+const onGetRoleUserList = async () => {
+  state.userListLoading = true
+  const res = await new RoleApi().getApplyRoseUserInfo({ roleid: state.roleId, name: state.filter.name }).catch(() => {
+    state.userListLoading = false
+  })
+  state.userListLoading = false
+  if (res?.success) {
+    if (res.data && res.data.length > 0) {
+      state.userListData = res.data
+    } else {
+      state.userListData = []
+    }
+  }
+}
+
+const onCurrentChange = (currentRow: RoleGetListOutput, oldCurrentRow: RoleGetListOutput) => {
+  if (!currentRow) {
+    return
+  }
+
+  if (currentRow?.id !== oldCurrentRow?.id && (oldCurrentRow?.id as number) > 0) {
+    if ((currentRow?.parentId as number) === 0) {
+      roleTableRef.value!.setCurrentRow(oldCurrentRow)
+    }
+  } else {
+    if ((currentRow?.parentId as number) === 0) {
+      roleTableRef.value!.setCurrentRow()
+    }
+  }
+
+  if ((currentRow?.parentId as number) !== 0 && (oldCurrentRow?.parentId as number) !== 0 && (currentRow?.id as number) > 0) {
+    state.roleId = currentRow.id
+    state.roleName = currentRow.name
+    onGetRoleUserList()
+  }
+}
+
+const onUserRowClick = (row: UserDto) => {
+  // TODO: improvement typing when refactor table
+  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+  // @ts-expect-error
+  userTableRef.value!.toggleRowSelection(row, undefined)
+}
+
+const onAddUser = () => {
+  if (!((state.roleId as number) > 0)) {
+    proxy.$modal.msgWarning('请选择角色')
+    return
+  }
+  userSelectRef.value.open({ roleId: state.roleId })
+}
+
+const onRemoveUser = () => {
+  if (!((state.roleId as number) > 0)) {
+    proxy.$modal.msgWarning('请选择角色')
+    return
+  }
+
+  const selectionRows = userTableRef.value!.getSelectionRows() as UserDto[]
+
+  if (!((selectionRows.length as number) > 0)) {
+    proxy.$modal.msgWarning('请选择用户')
+    return
+  }
+
+  proxy.$modal
+    .confirm(`确定要移除吗?`)
+    .then(async () => {
+      const userIds = selectionRows?.map((a) => ({id:a.id,name:a.name,mobile:a.mobile}))
+      await new RoleApi().deleteApplyRoseUser(userIds,{roleid:state.roleId} ,{ showSuccessMessage: true })
+      onGetRoleUserList()
+    })
+    .catch(() => {})
+}
+
+const onSureUser = async (users: UserDto[]) => {
+  if (!(users?.length > 0)) {
+    userSelectRef.value.close()
+    return
+  }
+
+  state.sureLoading = true
+  const userIds = users?.map((a) => ({id:a.id,name:a.name,mobile:a.mobile}))
+  await new RoleApi().addApplyRoseUser(userIds,{roleid:state.roleId} ,{ showSuccessMessage: true }).catch(() => {
+    state.sureLoading = false
+  })
+  state.sureLoading = false
+  userSelectRef.value.close()
+  onGetRoleUserList()
+}
+
+const onSetAppMenu = async (role: RoleGetListOutput) => {
+  if (!((role?.id as number) > 0)) {
+    proxy.$modal.msgWarning('请选择角色')
+    return
+  }
+  const input = { id: role.id, name:role.name }
+  const name = router.currentRoute.value.params.id
+  const appData=await new PermissionApi().getApplyInfo({Name:name})
+  console.log(appData.data)
+  setRoleMenuRef.value.open(input,appData.data[0].id)
+}
+
+const onSetRoleDataScope = (role: RoleGetListOutput) => {
+  if (!((role?.id as number) > 0)) {
+    proxy.$modal.msgWarning('请选择角色')
+    return
+  }
+  setRoleDataScopeRef.value.open(role)
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 202 - 0
admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/components/form-edit.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="admin-authorize-editInfo">
+    <el-dialog :title="formData.titleText" v-model="formData.isShowDialog" draggable width="769px">
+      <el-form :model="formData.editData"
+               :rules="rules"
+               ref="formRef"
+               v-loading="formData.loading"
+               size="default"
+               label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="软件类型" prop="softwareType">
+              <el-select  v-model="formData.editData.softwareType" placeholder="请选择软件类型" >
+                <el-option v-for="(value, key) in softwareType" :key="key" :label="value[1].name"  :value="Number(value[1].value)" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="设备类型" prop="equipmentType">
+              <el-select  v-model="formData.editData.equipmentType" placeholder="请选择设备类型" >
+                <el-option v-for="(value, key) in equipmentType" :key="key" :label="value[1].name"  :value="Number(value[1].value)" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="软件包名" prop="softwareName">
+              <el-input v-model="formData.editData.softwareName" placeholder="请输入软件包名" clearable></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="版本" prop="version">
+              <el-input v-model="formData.editData.version" placeholder="请输入版本" clearable></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="说明" prop="explain">
+              <el-input v-model="formData.editData.explain" placeholder="请输入说明" rows="6" clearable type="textarea"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="formData.editData.remark" placeholder="请输入备注" rows="6" clearable type="textarea"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+            <el-form-item label="数字签名" prop="digitalSignature">
+              <el-input v-model="formData.editData.digitalSignature" placeholder="数字签名,TQC+、ECVR、OPW必填" clearable></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <MyUploadFile  v-model="formData.fileValue" ref="uploadFileRef" :file="{fileUrl:formData.editData.downloadUrl,fileName:formData.editData.fileName}"  @onUpload="onUpload"/>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" icon="ele-CircleClose" size="default">取 消</el-button>
+          <el-button type="primary" :icon="formData.editIcon" @click="onSubmit" size="default">{{formData.buttonText}}</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {defineAsyncComponent, reactive, ref, watch} from "vue";
+import {FormRules} from "element-plus";
+import eventBus from "/@/utils/mitt";
+import {softwarePackageManagement_TableData} from "/@/api/admin/deviceAuthorization/softwarePackageManagementDto";
+import {useGlobalCacheStore} from "/@/stores/globalCacheStore";
+import {SoftwarePackageManagementApi} from "/@/api/admin/deviceAuthorization/softwarePackageManagementApi";
+//import { de } from "element-plus/es/locale";
+
+const MyUploadFile = defineAsyncComponent(() => import('/@/components/my-upload-file/index.vue'))
+
+/**获取全局缓存*/
+const globalCacheStore = useGlobalCacheStore()
+/**设备类型缓存*/
+const equipmentType = ref(globalCacheStore.getGlobalStore().get('equipmentType'))
+/**软件类型缓存*/
+const softwareType = ref(globalCacheStore.getGlobalStore().get('softwareType'))
+
+enum Form {
+  ADD,
+  UPDATE
+}
+
+
+/**数据对象 */
+const formData = reactive({
+  loading: false,
+  formType: Form,
+  titleText:'',
+  editIcon:'',
+  buttonText:'',
+  isShowDialog: false,
+  editData:{} as softwarePackageManagement_TableData,
+  fileValue: null,
+})
+/**有效版本号选择校验*/
+const validateVersion = (rule: any, value: any, callback: any) => {
+  const versionPattern: RegExp = /^\d+\.\d+\.\d+$/; // 正则表达式,以数字开头和结尾,并且包含数字和点号(.)
+  if (!versionPattern.test(value)) {
+    callback(new Error('输入的版本号格式错误'))
+  } else {
+    callback()
+  }
+}
+
+
+/**表单校验*/
+const rules = reactive<FormRules>({
+  softwareType: [
+    { required: true, message: '请选择软件类型', trigger: 'blur' },
+  ],
+  equipmentType: [
+    { required: true, message: '请选择设备类型', trigger: 'blur' },
+  ],
+  softwareName: [
+    { required: true, message: '请输入软件包名', trigger: 'blur' },
+  ],
+  version: [
+    { required: true, message: '请输入版本号', trigger: 'blur' },
+    { validator: validateVersion, trigger: 'blur' }
+  ],
+})
+
+/**
+ * 表单对象
+ */
+const formRef = ref()
+
+const openDialog = (val) => {
+  if(val){
+    formData.editData = JSON.parse(JSON.stringify(val))
+    formData.titleText = '更新软件包'
+    formData.buttonText = '更新'
+    //更新操作
+    formData.formType = Form.UPDATE
+    formData.editIcon = 'ele-Refresh'
+  }else {
+    formData.editData = {} as softwarePackageManagement_TableData
+    formData.titleText = '添加软件包'
+    formData.buttonText = '添加'
+    //添加操作
+    formData.formType = Form.ADD
+    formData.editIcon = 'ele-CirclePlus'
+  }
+  formData.isShowDialog = true
+  formData.isShowDialog = true
+}
+
+const onCancel = () => {
+  formData.editData = {} as softwarePackageManagement_TableData
+  formData.isShowDialog = false
+}
+
+/***监听弹窗关   闭表单验证*/
+watch(() => formData.isShowDialog,(newVal) => {
+  if(newVal) formRef.value?.resetFields()
+})
+
+const onUpload = (val) => {
+  formData.editData.downloadUrl = val.fileUrl
+  formData.editData.fileName = val.fileName
+  //MyUploadFile.value?.onSure();
+}
+
+const uploadFileRef = ref(null);
+
+const onSubmit =  () => {
+debugger
+  formRef.value.validate(async (valid: boolean) =>{
+    if(!valid) return
+
+    formData.loading = true
+    let res = {} as any
+    console.log(formData.editData)
+    if(formData.formType === Form.UPDATE){
+      res = await new SoftwarePackageManagementApi().addData(formData.editData as softwarePackageManagement_TableData)
+    }else if(formData.formType === Form.ADD){
+      res = await new SoftwarePackageManagementApi().addData(formData.editData as softwarePackageManagement_TableData)
+    }else {
+      formData.loading = false
+      return
+    }
+
+    if(res?.success){
+      eventBus.emit('refreshView')
+      formData.isShowDialog = false
+      formData.editData = {} as softwarePackageManagement_TableData
+    }
+
+    formData.loading = false
+  })
+}
+
+defineExpose({
+  openDialog,
+})
+</script>
+<style scoped lang="scss">
+
+</style>

+ 103 - 0
admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/components/table-download.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="admin-authorize-editInfo">
+      <el-dialog :title="titleText" v-model="isShowDialog" draggable width="769px">
+          <el-table stripe :data="softwareDownloadData.tableModel"
+               v-loading="softwareDownloadData.loading"
+               size="default"
+               style="height: 40vh" class="my-fill mt8" shadow="hover">
+          <el-table-column v-for="column in softwareDownloadData.dynamicColumns" :key="column.prop" :prop="column.prop" :label="column.label" align="center" >
+          </el-table-column>
+        </el-table>
+        <div class="my-flex my-flex-end" style="margin-top: 20px">
+            <el-pagination
+              v-model:currentPage="softwareDownloadData.pageInput.CurrentPage"
+              v-model:page-size="softwareDownloadData.pageInput.PageSize"
+              :total="softwareDownloadData.total"
+              :page-sizes="[10, 20, 50, 100]"
+              small
+              background
+              @size-change="onSizeChange"
+              @current-change="onCurrentChange"
+              layout="total, sizes, prev, pager, next, jumper"
+            />
+          </div>
+        <template #footer>
+          <span class="dialog-footer">
+            <el-button type="primary" icon="ele-CircleClose" @click="onClose" size="default">关闭</el-button>
+          </span>
+        </template>
+      </el-dialog>
+    </div>
+  </template>
+
+  <script setup lang="ts">
+  import { reactive, ref, watch} from "vue";
+  import {DownloadRecordDto,pageInputDownloadRecordDto} from "/@/api/admin/deviceAuthorization/softwarePackageManagementDto";
+  import {SoftwarePackageManagementApi} from "/@/api/admin/deviceAuthorization/softwarePackageManagementApi";
+
+
+  /**软件包下载记录管理对象*/
+const softwareDownloadData = reactive({
+  loading: false,
+  filterModel: {
+    fileId:0,
+    /**文件名称 */
+    fileName: "",
+    /**下载时间 */
+    downloadTime: "2020-11-22",
+    /**下载人员 */
+    downloadedBy: "",
+  },
+  total: 0,
+  pageInput: {
+    currentPage: 1,
+    pageSize: 10,
+  } as pageInputDownloadRecordDto,
+  tableModel: [] as Array<DownloadRecordDto>,
+  /**动态表头 */
+  dynamicColumns: [
+    { prop: 'fileName', label: '文件名称' },
+    { prop: 'downloadTime', label: '下载时间' },
+    { prop: 'downloadedBy', label: '下载人员' },
+  ]
+})
+
+  const isShowDialog=ref(false)
+  const titleText=ref('下载记录')
+  const formRef = ref()
+
+  const openDialog = async (id) => {
+    softwareDownloadData.filterModel.fileId=id
+    softwareDownloadData.loading = true
+    const res:any = await new SoftwarePackageManagementApi().getDownloadData({...softwareDownloadData.pageInput,filter:softwareDownloadData.filterModel})
+    softwareDownloadData.tableModel = res?.data?.list ?? []
+    softwareDownloadData.total = res?.data?.total ?? 0
+    softwareDownloadData.loading = false
+    isShowDialog.value = true
+  }
+  /**页条数变化*/
+const onSizeChange = () => {
+  openDialog(softwareDownloadData.filterModel.fileId)
+}
+
+/**页数变化*/
+const onCurrentChange = () =>{
+  openDialog(softwareDownloadData.filterModel.fileId)
+}
+  //关闭弹窗
+  const onClose = () => {
+    isShowDialog.value = false
+  }
+
+  /***监听弹窗关   闭表单验证*/
+  watch(() => isShowDialog.value,(newVal) => {
+    if(newVal) formRef.value?.resetFields()
+  })
+
+  defineExpose({
+    openDialog,
+  })
+  </script>
+  <style scoped lang="scss">
+
+  </style>

+ 375 - 0
admin.ui.plus-master/src/views/admin/application/softwarePackageManagement/index.vue

@@ -0,0 +1,375 @@
+<template>
+  <div class="layout-pd">
+    <el-row>
+      <!--操作-->
+      <el-col :xs="24">
+        <el-card class="mt8" shadow="hover">
+          <el-form :model="softwareData.Filter" @submit.stop.prevent>
+            <el-form-item prop="name" style="width: 100%">
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="软件类型">
+                  <el-select v-model="softwareData.Filter.softwareType" placeholder="请选择软件类型" style="width: 200px;">
+                    <el-option v-for="(value, key) in softwareType" :key="key" :label="value[1].name"
+                      :value="Number(value[1].value)" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="软件名称">
+                  <el-input v-model="softwareData.Filter.softwareName" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="版本">
+                  <el-input v-model="softwareData.Filter.version" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="设备类型">
+                  <el-select v-model="softwareData.Filter.equipmentType" placeholder="请选择设备类型" style="width: 200px;">
+                    <el-option v-for="(value, key) in equipmentType" :key="key" :label="value[1].name"
+                      :value="Number(value[1].value)" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="4" class="mb20">
+                <el-form-item label="选择时间">
+                  <el-date-picker v-model="softwareData.time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss"
+                    range-separator="To" start-placeholder="Start date" end-placeholder="End date" />
+                </el-form-item>
+              </el-col>
+            </el-form-item>
+          </el-form>
+
+          <hr>
+
+          <!-- 按钮  -->
+          <el-row justify="space-between" class="submit-button">
+            <el-row>
+              <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+              <el-button type="primary" icon="ele-RefreshRight" @click="onReset"> 重置 </el-button>
+            </el-row>
+            <el-row>
+              <el-button type="primary" icon="ele-CirclePlus" @click="onAdd"> 上传软件包 </el-button>
+            </el-row>
+          </el-row>
+        </el-card>
+      </el-col>
+      <!--表格-->
+      <el-col :xs="24">
+        <el-card style="height: 70vh" class="my-fill mt8" shadow="hover">
+          <el-table v-loading="softwareData.loading" stripe :data="softwareData.tableModel" row-key="id"
+            style="width: 100%">
+            <el-table-column v-for="column in softwareData.dynamicColumns" :key="column.prop" :prop="column.prop"
+              :label="column.label">
+              <template #default="{ row }">
+                <span :style="getColor(column.prop, row)">
+                  {{ getProp(column.prop, row) }}
+                </span>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" fixed="right" header-align="center" align="center" class="right-operation"
+              width="140">
+
+              <template #default="{ row }">
+                <el-link class="my-el-link mr12 ml12" v-if="row.state === 0" type="primary" icon="ele-Upload"
+                  @click="onRelease(row)" :underline="false" target="_blank">发布</el-link>
+                <el-link class="my-el-link mr12 ml12" v-if="row.state === 0" type="primary" icon="ele-Edit"
+                  @click="editTableData(row)" :underline="false" target="_blank">编辑</el-link>
+                <el-link class="my-el-link mr12 ml12" v-if="row.state === 1" type="primary" icon="ele-Download"
+                  @click="onDownload(row)" :underline="false" target="_blank">下载</el-link>
+                <el-link class="my-el-link mr12 ml12" v-if="row.state === 1" type="primary" icon="ele-WarningFilled"
+                  @click="onFailure(row)" :underline="false" target="_blank">失效</el-link>
+                <el-link class="my-el-link mr12 ml12" v-if="row.state !== 0" type="primary" icon="ele-Tickets"
+                  @click="onDownloadRecord(row.guid)" :underline="false" target="_blank">下载记录</el-link>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div class="my-flex my-flex-end" style="margin-top: 20px">
+            <el-pagination v-model:currentPage="softwareData.pageInput.CurrentPage"
+              v-model:page-size="softwareData.pageInput.PageSize" :total="softwareData.total"
+              :page-sizes="[10, 20, 50, 100]" small background @size-change="onSizeChange"
+              @current-change="onCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
+          </div>
+          <template>
+            <a ref="downloadLink" style="display: none;"></a>
+          </template>
+        </el-card>
+      </el-col>
+    </el-row>
+    <EditDialog ref="editDialogRef" />
+    <DownloadDialog ref="downloadDialogRef" />
+  </div>
+</template>
+
+<script setup lang="ts">
+
+import { defineAsyncComponent, onBeforeMount, onMounted, reactive, ref, watch } from "vue";
+import { useGlobalCacheStore } from "/@/stores/globalCacheStore";
+import {
+  softwarePackageManagement_AllDto,
+  softwarePackageManagement_SearchFilter,
+  softwarePackageManagement_TableData
+} from "/@/api/admin/deviceAuthorization/softwarePackageManagementDto";
+import type { pageInput } from "/@/api/admin/shareDto/shareDto";
+import eventBus from "/@/utils/mitt";
+import { SoftwarePackageManagementApi } from "/@/api/admin/deviceAuthorization/softwarePackageManagementApi";
+import { storeToRefs } from 'pinia'
+import { useUserInfo } from '/@/stores/userInfo'
+
+/**引入组件*/
+const EditDialog = defineAsyncComponent(() => import('./components/form-edit.vue'))
+const DownloadDialog = defineAsyncComponent(() => import('./components/table-download.vue'))
+// import {pageInput} from "/@/api/admin/shareDto/shareDto";
+const editDialogRef = ref()
+const downloadDialogRef = ref()
+
+
+/**获取全局缓存*/
+const globalCacheStore = useGlobalCacheStore()
+/**设备类型缓存*/
+const equipmentType = ref(globalCacheStore.getGlobalStore().get('equipmentType'))
+/**软件类型缓存*/
+const softwareType = ref(globalCacheStore.getGlobalStore().get('softwareType'))
+/**软件包状态缓存*/
+const softwarePackageStatus = ref(globalCacheStore.getGlobalStore().get('softwarePackageStatus'))
+
+/**软件包管理对象*/
+const softwareData = reactive({
+  time: '',
+  /**加载显示 */
+  loading: false,
+  /**条件查询模块 */
+  Filter: {
+    /**软件类型 */
+    softwareType: "",
+    /**软件名称 */
+    softwareName: "",
+    /**版本 */
+    version: "",
+    /**设备类型*/
+    equipmentType: "",
+    /**开始的时间 */
+    BeginTime: null,
+    /**开始的时间 */
+    EndTime: null,
+  } as softwarePackageManagement_SearchFilter,
+  /**表格信息 */
+  tableModel: [] as softwarePackageManagement_TableData,
+  /**动态表头 */
+  dynamicColumns: [
+    { prop: 'date', label: '日期' },
+    { prop: 'softwareType', label: '软件类型' },
+    { prop: 'equipmentType', label: '设备类型' },
+    { prop: 'softwareName', label: '软件名称' },
+    { prop: 'version', label: '版本号' },
+    { prop: 'explain', label: '说明' },
+    { prop: 'state', label: '状态' },
+    { prop: 'numberOfDownloads', label: '下载次数' },
+    { prop: 'remark', label: '备注' },
+    { prop: 'releaseTime', label: '发布日期' },
+    { prop: 'uploadTime', label: '上传日期' },
+    { prop: 'digitalSignature', label: '数字签名' },
+  ],
+  /**分页标识 */
+  pageInput: {
+    CurrentPage: 1,
+    PageSize: 10,
+  } as pageInput,
+  /**分页总数 */
+  total: 0,
+} as softwarePackageManagement_AllDto)
+
+const downloadLink = ref()
+/**条件查询 */
+const onQuery = () => {
+  init()
+}
+
+/**将Filter对象成.的连接方式*/
+const flattenObject = (obj, parentKey = '') => {
+  const result = {};
+  for (const key in obj) {
+    if (obj.hasOwnProperty(key)) {
+      const newKey = parentKey ? `${parentKey}.${key}` : key;
+      if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+        const flattened = flattenObject(obj[key], newKey);
+        Object.assign(result, flattened);
+      } else {
+        result[newKey] = obj[key];
+      }
+    }
+  }
+
+  return result;
+}
+
+/**初始化 */
+const init = async () => {
+  softwareData.loading = true
+  const query = flattenObject({ Filter: softwareData.Filter })
+  debugger
+  const res: any = await new SoftwarePackageManagementApi().getPageData({ ...softwareData.pageInput, Filter: softwareData.Filter })
+  softwareData.tableModel = res?.data?.list ?? []
+  softwareData.total = res?.data?.total ?? 0
+  softwareData.loading = false
+}
+
+onMounted(() => {
+  init()
+  eventBus.off('refreshView')
+  eventBus.on('refreshView', async () => {
+    await init()
+  })
+  console.log()
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshView')
+})
+
+/**
+ * 监听时间变换
+ */
+watch(() => softwareData.time, (newVal) => {
+  if (newVal.length === 0) {
+    return
+  }
+  softwareData.Filter.BeginTime = newVal?.[0].toString()
+  softwareData.Filter.EndTime = newVal?.[1].toString()
+})
+
+/**重置查询条件 */
+const resetQuery = () => {
+  softwareData.Filter.softwareType = ''
+  softwareData.Filter.softwareName = ''
+  softwareData.Filter.version = ''
+  softwareData.Filter.equipmentType = ''
+  softwareData.Filter.BeginTime = ''
+  softwareData.Filter.EndTime = ''
+  softwareData.time = ''
+  softwareData.pageInput.CurrentPage = 1
+}
+
+/**重置 */
+const onReset = () => {
+  resetQuery()
+  init()
+}
+
+/**添加*/
+const onAdd = () => {
+  editDialogRef.value.openDialog()
+}
+
+/**编辑弹窗 */
+const editTableData = (row) => {
+  editDialogRef.value.openDialog(row)
+}
+
+/**发布*/
+const onRelease = (row) => {
+  debugger
+  //downloadUrl
+  softwareData.loading = true;
+  (row as softwarePackageManagement_TableData).state = 1
+  var RowData = JSON.parse(JSON.stringify(row))
+  var res = new SoftwarePackageManagementApi().addData(RowData as softwarePackageManagement_TableData)
+  softwareData.loading = false
+}
+
+/**下载*/
+const onDownload = (row) => {
+  softwareData.loading = true;
+  var RowData = JSON.parse(JSON.stringify(row))
+  const params = RowData as softwarePackageManagement_TableData
+  //const link = downloadLink.value;
+  //const url = `http://localhost:8000/api/app/software/download-file`;
+  //link.href = url;
+  //link.click();
+  const filename = params.fileName?.toString();
+  const { userInfos } = storeToRefs(useUserInfo());
+  const accessToken = userInfos.value.token
+  const token = `Bearer ${accessToken}`
+  var myHeaders = new Headers();
+  myHeaders.append("Authorization", token);
+
+  const requestOptions: RequestInit = {
+    method: 'GET',
+    headers: myHeaders,
+    redirect: 'follow', // 此处使用枚举值 'follow'
+  };
+
+  /* var res =  new SoftwarePackageManagementApi().DownloadFile(row as softwarePackageManagement_TableData) */
+  const fileUrl = import.meta.env.VITE_API_URL + '/api/app/software/download-file?guid=' + params.guid; // 替换为实际文件URL
+  fetch(fileUrl, requestOptions)
+    .then(response => response.blob())
+    .then(blob => {
+      const link = document.createElement('a');
+      link.href = URL.createObjectURL(blob);
+      link.download = filename;
+      link.click();
+      softwareData.loading = false
+    })
+    .catch(error => {
+      console.error('下载失败:', error);
+      softwareData.loading = false
+    });
+  // var res =  new SoftwarePackageManagementApi().DownloadFile(RowData as softwarePackageManagement_TableData)
+
+}
+
+/**失效*/
+const onFailure = (row) => {
+  softwareData.loading = true;
+  (row as softwarePackageManagement_TableData).state = 2
+  var RowData = JSON.parse(JSON.stringify(row))
+  var res = new SoftwarePackageManagementApi().addData(RowData as softwarePackageManagement_TableData)
+  softwareData.loading = false
+}
+
+/**下载记录*/
+const onDownloadRecord = (id) => {
+  downloadDialogRef.value.openDialog(id)
+  //alert('下载记录')
+}
+
+/**页条数变化*/
+const onSizeChange = () => {
+  init()
+}
+
+/**页数变化*/
+const onCurrentChange = () => {
+  init()
+}
+
+/**过滤数字的属性*/
+const getProp = (val, row) => {
+  //debugger
+  // var sss = row[val];
+  // var fdsdfsd = String(row[val]);
+  //if(!row[val]) return null
+  if (val === 'softwareType') return softwareType.value.get(String(row[val])).name
+  if (val === 'equipmentType') return equipmentType.value.get(String(row[val])).name
+  if (val === 'state') return softwarePackageStatus.value.get(String(row[val])).name
+  return row[val]
+}
+
+const getColor = (val, row) => {
+  if (val === 'state') {
+    const code = softwarePackageStatus.value.get(String(row[val])).code.split('_')
+    return { color: code[1] }
+  }
+  return {}
+}
+
+</script>
+
+<style scoped lang="scss">
+@import '/@/theme/tableStyle.scss';
+.el-link {
+  padding: 5px;
+}
+
+</style>

+ 160 - 0
admin.ui.plus-master/src/views/admin/application/viewManage/components/view-form.vue

@@ -0,0 +1,160 @@
+<template>
+  <div>
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="600px"
+    >
+      <el-form :model="form" ref="formRef" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="应用" prop="label">
+              <el-input v-model="state.app" clearable disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="上级视图">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="viewTreeData"
+                node-key="id"
+                check-strictly
+                default-expand-all
+                render-after-expand
+                fit-input-width
+                clearable
+                class="w100"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图名称" prop="label" :rules="[{ required: true, message: '请输入视图名称', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.label" clearable />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图命名" prop="path">
+              <el-input v-model="form.name" clearable />
+            </el-form-item>
+          </el-col> -->
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图地址" prop="path">
+              <el-input v-model="form.path" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
+            <el-form-item label="排序">
+              <el-input-number v-model="form.sort" />
+            </el-form-item>
+          </el-col>
+          <!-- <el-col :xs="24" :sm="7" :md="7" :lg="7" :xl="7">
+            <el-form-item label="缓存">
+              <el-switch v-model="form.cache" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="7" :md="7" :lg="7" :xl="7">
+            <el-form-item label="启用">
+              <el-switch v-model="form.enabled" />
+            </el-form-item>
+          </el-col> -->
+          <!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="视图描述" prop="description">
+              <el-input v-model="form.description" clearable type="textarea" />
+            </el-form-item>
+          </el-col> -->
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/view/form">
+import { reactive, toRefs, ref, PropType } from 'vue'
+import { OauthView,OauthViewDto } from '/@/api/admin/data-contracts'
+import { ViewApi } from '/@/api/admin/View'
+import { PermissionApi } from '/@/api/admin/Permission'
+import eventBus from '/@/utils/mitt'
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  viewTreeData: {
+    type: Array as PropType<OauthView[]>,
+    default: () => [],
+  },
+})
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,
+  form: {} as OauthViewDto,
+  app:''
+})
+const { form } = toRefs(state)
+
+// 打开对话框
+const open = async (app:any,row: any = {}) => {
+  // if (row.id > 0) {
+  //   const res = await new PermissionApi().getViewInfo({ id: row.id }, { loading: true })
+
+  //   if (res?.success) {
+  //     let formData = res.data as OauthViewDto
+  //     state.form = formData
+  //   }
+  // } else {
+  //   state.form = {} as OauthViewDto
+  // }
+  state.app=app
+  state.form=row
+  state.showDialog = true
+}
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.sureLoading = true
+    let res = {} as any
+    const appData=await new PermissionApi().getApplyInfo({name:state.app})
+    state.form.appID=appData.data[0].id
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new PermissionApi().addView(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new PermissionApi().addView(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshView')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 140 - 0
admin.ui.plus-master/src/views/admin/application/viewManage/index.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :model="state.filter" :inline="true" @submit.stop.prevent>
+        <el-form-item label="应用">
+          <el-input v-model="state.filter.app" disabled placeholder="请选择应用" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item label="视图名称">
+          <el-input v-model="state.filter.name" placeholder="视图名称" @keyup.enter="onQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+          <el-button v-auth="'api:admin:view:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table
+        :data="state.viewTreeData"
+        style="width: 100%"
+        v-loading="state.loading"
+        row-key="id"
+        default-expand-all
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+        <el-table-column prop="label" label="视图名称" min-width="120" show-overflow-tooltip />
+        <!-- <el-table-column prop="name" label="视图命名" min-width="120" show-overflow-tooltip /> -->
+        <el-table-column prop="path" label="视图地址" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="sort" label="排序" width="80" align="center" show-overflow-tooltip />
+        <!-- <el-table-column prop="description" label="视图描述" min-width="120" show-overflow-tooltip /> -->
+        <!-- <el-table-column label="状态" width="80" align="center" show-overflow-tooltip>
+          <template #default="{ row }">
+            <el-tag type="success" v-if="row.enabled">启用</el-tag>
+            <el-tag type="danger" v-else>禁用</el-tag>
+          </template>
+        </el-table-column> -->
+        <el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
+          <template #default="{ row }">
+            <el-button v-auth="'api:admin:view:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
+            <el-button v-auth="'api:admin:view:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+
+    <view-form ref="viewFormRef" :title="state.viewFormTitle" :view-tree-data="state.viewTreeData"></view-form>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/view">
+import router from "/@/router";
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
+import { OauthView,OauthApply } from '/@/api/admin/data-contracts'
+import { ViewApi } from '/@/api/admin/View'
+import { listToTree, filterTree } from '/@/utils/tree'
+import { cloneDeep } from 'lodash-es'
+import { PermissionApi } from '/@/api/admin/Permission'
+import eventBus from '/@/utils/mitt'
+
+// 引入组件
+const ViewForm = defineAsyncComponent(() => import('./components/view-form.vue'))
+
+const viewFormRef = ref()
+const { proxy } = getCurrentInstance() as any
+
+const state = reactive({
+  loading: false,
+  viewFormTitle: '',
+  filter: {
+    name: '',
+    app:''
+  },
+  viewTreeData: [] as Array<OauthView>,
+  formViewTreeData: [] as Array<OauthView>
+})
+
+onMounted(() => {
+  onQuery()
+  eventBus.off('refreshView')
+  eventBus.on('refreshView', async () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshView')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  const name = router.currentRoute.value.params.id
+  const appData=await new PermissionApi().getApplyInfo({Name:name})
+  state.filter.app=appData.data[0].name
+  const res = await new PermissionApi().getViewInfo({appid:appData.data[0].id,label:state.filter.name}).catch(() => {
+    state.loading = false
+  })
+  res.data=res.data.map((item)=>{
+    if(item.parentId===''||item.parentId===null){
+      item.parentId=Number('0')
+      item.type=1
+    }
+    item.parentId=Number(item.parentId)
+    return item
+  })
+  if (res && res.data && res.data.length > 0) {
+    state.viewTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.name, {
+      filterWhere: (item: any, keyword: string) => {
+        return item.label?.toLocaleLowerCase().indexOf(keyword) > -1 || item.path?.toLocaleLowerCase().indexOf(keyword) > -1
+      },
+    })
+  } else {
+    state.viewTreeData = []
+    state.formViewTreeData = []
+  }
+  state.loading = false
+}
+
+const onAdd = () => {
+  state.viewFormTitle = '新增视图'
+  viewFormRef.value.open(state.filter.app)
+}
+
+const onEdit = (row: OauthView) => {
+  state.viewFormTitle = '编辑视图'
+  viewFormRef.value.open(state.filter.app,row)
+}
+
+const onDelete = (row: OauthView) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除视图【${row.label}】?`)
+    .then(async () => {
+      await new PermissionApi().viewDelete({ id: row.id }, { loading: true })
+      onQuery()
+    })
+    .catch(() => {})
+}
+</script>
+
+<style scoped lang="scss"></style>

+ 163 - 0
admin.ui.plus-master/src/views/admin/statement/BlackBox/index.vue

@@ -0,0 +1,163 @@
+
+<template>
+  <div class="layout-pd">
+    <el-row>
+<!--操作-->
+      <el-col :xs="24" >
+        <el-card class="mt8"  shadow="hover" >
+<el-form :model="state.filter" :inline="true" @submit.stop.prevent>
+            <el-form-item prop="name" style="width: 100%">
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+                <el-form-item label="石油公司">
+                  <el-input v-model="state.filter.OilCompanyName" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+                <el-form-item label="序列号">
+                  <el-input v-model="state.filter.SerialNumber" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+                <el-form-item label="绑定加油机厂商">
+                  <el-input v-model="state.filter.BindingDispenserManufacturer" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+                <el-form-item label="安全装置状态">
+                  <el-input v-model="state.filter.DeviceStatus" placeholder="单行输入" clearable></el-input>
+                </el-form-item>
+              </el-col>
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+            <el-form-item label="选择时间">
+                  <el-date-picker
+                    v-model="state.filter.FixTime"
+                    type="datetimerange"
+                    value-format="YYYY-MM-DD HH:mm:ss"
+                    range-separator="To"
+                    start-placeholder="Start date"
+                    end-placeholder="End date"
+                  />
+                </el-form-item>
+              </el-col>
+<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
+            <el-form-item label="选择时间">
+                  <el-date-picker
+                    v-model="state.filter.SoldTime"
+                    type="datetimerange"
+                    value-format="YYYY-MM-DD HH:mm:ss"
+                    range-separator="To"
+                    start-placeholder="Start date"
+                    end-placeholder="End date"
+                  />
+                </el-form-item>
+              </el-col>
+</el-form-item>
+          </el-form>
+<div class="my-flex my-flex-start" >
+<el-button  type="primary" icon="ele-CirclePlus" @click="onQuery"> 查询 </el-button>
+</div>
+</el-card>
+      </el-col>
+<!--表格-->
+      <el-col  :xs="24" >
+        <el-card style="height: 70vh" class="my-fill mt8" shadow="hover">
+<el-table v-loading="state.loading" stripe :data="state.tableModel" row-key="id" style="width: 100%">
+        <el-table-column v-for="column in state.dynamicColumns" :key="column.prop" :prop="column.prop" :label="column.label"  >
+        </el-table-column>
+</el-table>
+<div class="my-flex my-flex-end" style="margin-top: 20px">
+            <el-pagination
+              v-model:currentPage="state.pageInput.CurrentPage"
+              v-model:page-size="state.pageInput.PageSize"
+              :total="state.total"
+              :page-sizes="[10, 20, 50, 100]"
+              small
+              background
+              @size-change="onSizeChange"
+              @current-change="onCurrentChange"
+              layout="total, sizes, prev, pager, next, jumper"
+            />
+          </div>
+</el-card>
+      </el-col>
+</el-row>
+    </div>
+  </template>
+  <script setup lang="ts">
+  import {onBeforeMount, onMounted, reactive, ref, watch} from "vue";
+  import eventBus from "/@/utils/mitt";
+  import { PageInput,BlackBoxDto } from "/@/api/admin/reportManagement/BlackBox/BlackBoxdto";
+  import {Api} from "/@/api/admin/reportManagement/BlackBox/BlackBoxapi";
+/**数据对象*/
+  const state = reactive({
+    /**加载显示 */
+    loading: false,
+    /**条件查询模块 */
+      filter: {
+/**石油公司 */
+        OilCompanyName: "",
+/**序列号 */
+        SerialNumber: "",
+/**绑定加油机厂商 */
+        BindingDispenserManufacturer: "",
+/**安全装置状态 */
+        DeviceStatus: "",
+/**安装时间 */
+        FixTime: "",
+/**出厂时间 */
+        SoldTime: "",},
+  /**表格信息 */
+tableModel: [] as BlackBoxDto,
+    /**动态表头 */
+    dynamicColumns: [
+{ prop: 'oilCompanyName', label: '石油公司' },
+{ prop: 'softwareVersion', label: '软件版本号' },
+{ prop: 'hardwareVersion', label: '硬件版本号' },
+{ prop: 'serialNumber', label: '序列号' },
+{ prop: 'bindingDispenserManufacturer', label: '绑定加油机厂商' },
+{ prop: 'componentManufacturer', label: '部件出厂加油机厂商' },
+{ prop: 'stationName', label: '加油站名称' },
+{ prop: 'safetyDeviceManufacturer', label: '安全装置厂家' },
+{ prop: 'dispenserNumber', label: '油机号' },
+{ prop: 'nozzleNumber', label: '油枪号' },
+{ prop: 'deviceStatus', label: '安全装置状态' },
+{ prop: 'fixTime', label: '安装时间' },
+{ prop: 'soldTime', label: '出厂时间' },],
+/**分页标识 */
+    pageInput:{
+      currentPage: 1,
+      pageSize: 10,
+    } as PageInput,
+    /**分页总数 */
+    total: 0,
+})
+  onMounted(() => {
+    init()
+    eventBus.off('refreshView')
+    eventBus.on('refreshView', async () => {
+      await init()
+    })
+    console.log()
+  })
+  onBeforeMount(() => {
+    eventBus.off('refreshView')
+  })
+  /**
+  * 监听变换
+  */
+  watch(() => {})
+/**条件查询 */
+          const onQuery = () => {
+          init()
+        }
+        /**初始化 */
+        const init = async () => {
+          state.loading = true
+const res:any = await new Api().getList({...state.pageInput, Filter:state.filter})
+            state.total = res?.data?.total ?? 0
+state.tableModel = res?.data?.list ?? []
+          state.loading = false
+        }
+</script>
+<style scoped lang="scss">
+  </style>