소스 검색

feature: 首页组件迁移

ljc 7 달 전
부모
커밋
c1a6030403
41개의 변경된 파일3824개의 추가작업 그리고 2231개의 파일을 삭제
  1. BIN
      src/assets/image/logo.png
  2. BIN
      src/assets/image/logo2.png
  3. BIN
      src/assets/image/we.png
  4. 20 0
      src/components/BreadCrumb.vue
  5. 291 0
      src/components/NavMenu.vue
  6. 283 0
      src/components/TabMenu.vue
  7. 135 135
      src/layout/LayoutColumns/index.vue
  8. 4 8
      src/layout/components/Header/components/Avatar.vue
  9. 1 1
      src/main.ts
  10. 10 17
      src/routers/index.ts
  11. 119 0
      src/routers/modules/uCard.ts
  12. 17 0
      src/service/apply.ts
  13. 24 0
      src/service/customer.ts
  14. 25 0
      src/service/ucard.ts
  15. 651 0
      src/views/card/CardOrder/index.vue
  16. 127 0
      src/views/components/DetailedInfoCid/index.scss
  17. 1049 0
      src/views/components/DetailedInfoCid/index.vue
  18. 1 154
      src/views/home/index.vue
  19. 749 0
      src/views/home2/Home.vue
  20. 0 111
      src/views/login/components/LoginForm.vue
  21. 0 14
      src/views/login/components/LoginQrcode.vue
  22. 184 0
      src/views/page/Page.vue
  23. 134 0
      src/views/page/cachedRoute.ts
  24. 0 99
      src/views/system/dept/components/deptDialog.vue
  25. 0 41
      src/views/system/dept/index.scss
  26. 0 130
      src/views/system/dept/index.vue
  27. 0 117
      src/views/system/dictionary/components/Side.vue
  28. 0 160
      src/views/system/dictionary/components/Table.vue
  29. 0 96
      src/views/system/dictionary/components/dictionaryEntryDialog.vue
  30. 0 95
      src/views/system/dictionary/components/dictsortDialog.vue
  31. 0 106
      src/views/system/dictionary/index.scss
  32. 0 22
      src/views/system/dictionary/index.vue
  33. 0 84
      src/views/system/menu/components/MenuDrawer.vue
  34. 0 133
      src/views/system/menu/index.vue
  35. 0 90
      src/views/system/role/components/roleDrawer.vue
  36. 0 168
      src/views/system/role/index.vue
  37. 0 118
      src/views/system/user/components/userDialog.vue
  38. 0 64
      src/views/system/user/components/userSide.vue
  39. 0 154
      src/views/system/user/components/userTable.vue
  40. 0 99
      src/views/system/user/index.scss
  41. 0 15
      src/views/system/user/index.vue

BIN
src/assets/image/logo.png


BIN
src/assets/image/logo2.png


BIN
src/assets/image/we.png


+ 20 - 0
src/components/BreadCrumb.vue

@@ -0,0 +1,20 @@
+<template>
+  <el-breadcrumb separator="/">
+    <el-breadcrumb-item v-for="(item, key) in breadCrumbs" :key="key">{{
+      $t(item.name)
+    }}</el-breadcrumb-item>
+  </el-breadcrumb>
+</template>
+
+<script lang="ts" setup>
+  import { useRoute } from 'vue-router'
+  import { computed } from 'vue'
+  const route = useRoute()
+  const breadCrumbs = computed(() => {
+    return route.matched.filter((item) => {
+      if (item.meta.OnBreadCrumb) return true
+    })
+  })
+</script>
+
+<style scoped lang="scss"></style>

+ 291 - 0
src/components/NavMenu.vue

@@ -0,0 +1,291 @@
+<template>
+  <el-menu id="NavMenu" :default-openeds="['0']" :collapse="isCollapse">
+    <div class="logo">
+      <img v-if="isCollapse" class="img2" :src="logo2" alt="" />
+      <img v-else class="img1" src="logo" alt="" />
+    </div>
+    <div v-for="(item, key) in menus" :key="key">
+      <el-submenu v-if="item.show && item.name != 'R-Shop'" :index="key + ''">
+        <template #title>
+          <i class="iconfont" :class="item.icon"></i>
+          <span class="title">{{ $t(item.name) }}</span>
+        </template>
+        <div v-for="(subitem, subkey) in item.children" :key="key + '-' + subkey">
+          <el-menu-item
+            v-if="subitem.show && subitem.name != 'R-System-QAList'"
+            :index="key + '-' + subkey"
+            @click="routerPush(subitem.link)"
+          >
+            <template #title>
+              <span class="title">{{ $t(subitem.name) }}</span>
+            </template>
+          </el-menu-item>
+        </div>
+      </el-submenu>
+      <div
+        v-if="item.show && item.name == 'R-Shop' && !isCollapse"
+        class="points"
+        @click="toPointsMall(item)"
+      >
+        <a :href="item.link" target="_blank">
+          <i class="el-icon-shopping-cart-full"></i>
+          <span>{{ $t(item.name) }}</span>
+        </a>
+      </div>
+      <div
+        v-if="item.show && item.name == 'R-Shop' && isCollapse"
+        class="points1"
+        @click="toPointsMall(item)"
+      >
+        <a :href="item.link" target="_blank">
+          <el-tooltip class="item" effect="dark" :content="$t(item.name)" placement="right">
+            <i class="el-icon-shopping-cart-full" style="display: inline"></i>
+          </el-tooltip>
+        </a>
+      </div>
+    </div>
+  </el-menu>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, onMounted, getCurrentInstance } from 'vue'
+  import { useRoute, useRouter } from 'vue-router'
+  import { useStore, mapState } from 'vuex'
+  import session from '@/lib/session' // 假设 session 已转换为 ES module
+  // 引入图片
+  import logo2 from '@/assets/image/logo2.png'
+  import logo from '@/assets/image/logo.png'
+  const v3This = getCurrentInstance()
+
+  // 组合式 API
+  const route = useRoute()
+  const router = useRouter()
+  const store = useStore()
+
+  // 响应式数据
+  const upath = ref('/')
+
+  // 计算属性 - 使用 mapState 的等效方式
+  const menus = computed(() => store.state.navMenu.menus)
+  const isCollapse = computed(() => store.state.navMenu.isCollapse)
+
+  const routerPush = (path) => {
+    if (route.path === path || path === '#') {
+      return
+    }
+
+    upath.value = path
+    const menuItem = findMenuItemByPath(path)
+
+    // 使用事件总线或 provide/inject 替代 $bus
+    // 这里假设使用 mitt 或自定义事件系统
+    v3This.ctx.$emit('menu-click', {
+      link: path,
+      name: menuItem ? menuItem.name : route.name,
+      icon: menuItem ? menuItem.icon : 'el-icon-document',
+    })
+
+    router.push(path)
+  }
+
+  const findMenuItemByPath = (path) => {
+    for (const menu of menus.value) {
+      if (menu.children) {
+        for (const subMenu of menu.children) {
+          if (subMenu.link === path) {
+            return {
+              name: subMenu.name,
+              icon: menu.icon || 'el-icon-document',
+            }
+          }
+        }
+      }
+    }
+    return null
+  }
+
+  const toPointsMall = (item) => {
+    console.log(item)
+  }
+
+  // 初始化菜单权限
+  const initMenuPermissions = () => {
+    menus.value.forEach((item) => {
+      if (item.name === 'R-User') {
+        const visibleChildren = item.children.filter((el) => el.show)
+        if (visibleChildren.length === 1 && visibleChildren[0].name === 'R-UserList') {
+          if (visibleChildren[0].btns['R-UserList-Search'].show === false) {
+            item.show = false
+          }
+        } else {
+          const obj = visibleChildren.find((el) => el.name === 'R-UserList')
+          if (obj && obj.btns['R-UserList-Search'].show === false) {
+            obj.show = false
+          }
+        }
+      }
+    })
+    console.log(menus.value)
+  }
+
+  // 生命周期
+  onMounted(() => {
+    // 登陆后调用
+    const userData = session.Get('user', true)
+    if (userData) {
+      store.commit('InitNavMenu', JSON.parse(userData).display)
+      initMenuPermissions()
+    }
+  })
+
+  // 如果需要暴露给模板
+  defineExpose({
+    routerPush,
+    toPointsMall,
+  })
+</script>
+
+<style scoped lang="scss">
+  #NavMenu {
+    border-right: none;
+    .logo {
+      height: 100px;
+      overflow: hidden;
+      .img1 {
+        height: 50px;
+        margin-top: 20px;
+      }
+      .img2 {
+        height: 39px;
+        margin-top: 28px;
+      }
+    }
+    background-color: rgba(43, 48, 67, 1);
+    color: #6b7188;
+    &:not(.el-menu--collapse) {
+      width: 250px;
+      min-height: 100%;
+    }
+    .title {
+      margin-left: 10px;
+    }
+    .points {
+      padding: 15px 5px;
+      font-size: 14px;
+      color: #ffffff;
+      text-align: center;
+      a {
+        display: inline-block;
+        color: #ffffff;
+        padding: 6px 10px;
+        background-color: #eb3f57;
+        border-radius: 50px;
+        width: 100px;
+        display: flex;
+        justify-content: space-around;
+        align-content: center;
+        align-items: center;
+        margin: auto;
+        i {
+          margin-right: 10px;
+          font-size: 18px;
+        }
+        span {
+          font-size: 14px;
+        }
+      }
+    }
+    .points1 {
+      padding: 15px 5px;
+      font-size: 14px;
+      color: #ffffff;
+      text-align: center;
+      a {
+        display: inline-block;
+        color: #ffffff;
+        padding: 6px;
+        background-color: #eb3f57;
+        border-radius: 50px;
+        width: 20px;
+        height: 20px;
+        display: flex;
+        align-content: center;
+        align-items: center;
+        margin: auto;
+        i {
+          margin-right: 10px;
+          font-size: 18px;
+        }
+        span {
+          font-size: 14px;
+        }
+      }
+    }
+  }
+</style>
+
+<style lang="scss">
+  #NavMenu.el-menu--collapse.el-menu .el-submenu .el-submenu__title .el-submenu__icon-arrow {
+    display: none;
+  }
+  #NavMenu.el-menu--collapse .el-submenu .el-submenu__title .title {
+    height: 0;
+    width: 0;
+    overflow: hidden;
+    visibility: hidden;
+    display: inline-block;
+  }
+  #NavMenu {
+    .el-submenu {
+      text-align: left;
+      .el-submenu__title {
+        font-weight: 700;
+        color: #6b7188;
+        &:hover {
+          color: #ffffff;
+          background-color: rgba(43, 48, 67, 1);
+        }
+        i {
+          color: #ffffff;
+        }
+      }
+      .el-menu {
+        margin-left: 1.5em;
+
+        &:focus {
+          background-color: rgba(43, 48, 67, 1);
+        }
+        background-color: rgba(43, 48, 67, 1);
+        li {
+          color: #a4adcd;
+        }
+        .is-active {
+          background-color: rgba(43, 48, 67, 1);
+          color: #ffffff !important;
+        }
+        .el-menu-item {
+          &:hover {
+            color: #ffffff;
+            background-color: rgba(43, 48, 67, 1);
+          }
+        }
+      }
+    }
+  }
+  .el-menu {
+    background-color: rgba(43, 48, 67, 1) !important;
+    overflow: hidden;
+    margin-left: 0 !important;
+  }
+  .el-menu-item {
+    font-weight: 400;
+    color: #a4adcd !important;
+    font-size: 12px;
+    background-color: rgba(43, 48, 67, 1);
+    &:focus,
+    &:hover {
+      color: #ffffff !important;
+      background-color: rgba(43, 48, 67, 1) !important;
+    }
+  }
+</style>

+ 283 - 0
src/components/TabMenu.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="tab-menu">
+    <el-tabs
+      v-model="activeTab"
+      type="card"
+      closable
+      class="dynamic-tabs"
+      @tab-remove="removeTab"
+      @tab-click="handleTabClick"
+    >
+      <el-tab-pane
+        v-for="tab in tabs"
+        :key="tab.path"
+        :label="tab.title"
+        :name="tab.path"
+        :closable="tab.path !== '/enter/dashboard'"
+      >
+        <template #label>
+          <span>
+            <i v-if="tab.icon" :class="tab.icon"></i>
+            {{ tab.title }}
+          </span>
+        </template>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
+  import { useRoute, useRouter } from 'vue-router'
+  import { useStore } from 'vuex'
+  import { useI18n } from 'vue-i18n'
+
+  // 组合式 API
+  const route = useRoute()
+  const router = useRouter()
+  const store = useStore()
+  const { t, locale } = useI18n()
+
+  // 响应式数据
+  const tabs = ref([
+    {
+      title: t('Home'),
+      path: '/enter/dashboard',
+      icon: 'el-icon-house',
+      closable: false,
+    },
+  ])
+  const activeTab = ref('/enter/dashboard')
+
+  // 计算属性
+  const menus = computed(() => store.state.navMenu.menus)
+
+  // 方法
+  const extractNumberFromLink = (link) => {
+    if (!link) return null
+    const match = link.match(/(\d+)$/)
+    return match ? parseInt(match[1], 10) : null
+  }
+
+  const addTab = (route) => {
+    let routeName = route.name
+    if (route.name === 'R-CustomerDetail') {
+      const linkNumber = extractNumberFromLink(route.path)
+      routeName = `${t('CustomerDetail')}(${linkNumber})`
+    }
+
+    const path = route.path
+    const name = routeName || route.name
+    const icon = route.icon
+
+    // 检查tab是否已存在
+    const existingTab = tabs.value.find((tab) => tab.path === path)
+    if (existingTab) {
+      activeTab.value = path
+      return
+    }
+
+    // 从菜单中查找对应的菜单项
+    let menuItem = findMenuItemByPath(path)
+
+    if (!menuItem) {
+      // 如果没有找到对应的菜单项,使用路由信息
+      menuItem = {
+        name: name || path,
+        icon: icon || 'el-icon-document',
+      }
+    }
+
+    const newTab = {
+      title: t(menuItem.name) || menuItem.name || name || path,
+      path: path,
+      icon: menuItem.icon || 'el-icon-document',
+      closable: path !== '/enter/dashboard',
+    }
+
+    tabs.value.push(newTab)
+    activeTab.value = path
+  }
+
+  const updateTabTitles = () => {
+    tabs.value.forEach((tab) => {
+      // 特殊处理CID详情的情况
+      if (tab.path.includes('/customer/detail/')) {
+        const linkNumber = extractNumberFromLink(tab.path)
+        tab.title = `${t('CustomerDetail')}(${linkNumber})`
+        return
+      }
+
+      // 重新获取菜单项信息
+      const menuItem = findMenuItemByPath(tab.path)
+      if (menuItem) {
+        const newTitle = t(menuItem.name) || menuItem.name
+        tab.title = newTitle
+      }
+    })
+  }
+
+  const findMenuItemByPath = (path) => {
+    for (const menu of menus.value) {
+      if (menu.children) {
+        for (const subMenu of menu.children) {
+          if (subMenu.link === path) {
+            return {
+              name: subMenu.name,
+              icon: menu.icon || 'el-icon-document',
+            }
+          }
+        }
+      }
+    }
+    return null
+  }
+
+  const removeTab = (targetPath) => {
+    // 不允许关闭首页tab
+    if (targetPath === '/enter/dashboard') {
+      return
+    }
+
+    const currentTabs = tabs.value
+    let activePath = activeTab.value
+
+    if (activePath === targetPath) {
+      currentTabs.forEach((tab, index) => {
+        if (tab.path === targetPath) {
+          const nextTab = currentTabs[index + 1] || currentTabs[index - 1]
+          if (nextTab) {
+            activePath = nextTab.path
+          }
+        }
+      })
+    }
+
+    tabs.value = currentTabs.filter((tab) => tab.path !== targetPath)
+    activeTab.value = activePath
+
+    // 跳转到激活的tab
+    if (route.path !== activePath) {
+      router.push(activePath)
+    }
+  }
+
+  const handleTabClick = (tab) => {
+    const path = tab.props.name
+    if (route.path !== path) {
+      router.push(path)
+    }
+  }
+
+  const handleMenuClick = (menuItem) => {
+    addTab({
+      path: menuItem.link,
+      name: menuItem.name,
+      icon: menuItem.icon,
+    })
+  }
+
+  // 事件监听
+  const setupEventListeners = () => {
+    // 使用事件总线或自定义事件
+    window.addEventListener('menu-click', (event) => {
+      handleMenuClick(event.detail)
+    })
+  }
+
+  const removeEventListeners = () => {
+    window.removeEventListener('menu-click', handleMenuClick)
+  }
+
+  // 监听器
+  watch(
+    route,
+    (newRoute) => {
+      addTab(newRoute)
+    },
+    { immediate: true }
+  )
+
+  watch(
+    locale,
+    () => {
+      updateTabTitles()
+    },
+    { immediate: true }
+  )
+
+  // 生命周期
+  onMounted(() => {
+    setupEventListeners()
+  })
+
+  onUnmounted(() => {
+    removeEventListeners()
+  })
+</script>
+
+<style scoped lang="scss">
+  .tab-menu {
+    background: #fff;
+    border-bottom: 1px solid #e4e7ed;
+    padding: 0;
+    margin-bottom: 15px;
+
+    .dynamic-tabs {
+      .el-tabs__header {
+        margin: 0;
+
+        .el-tabs__nav-wrap {
+          .el-tabs__nav-scroll {
+            .el-tabs__nav {
+              .el-tabs__item {
+                height: 40px;
+                line-height: 40px;
+                padding: 0 20px;
+                font-size: 14px;
+                color: #606266;
+                border: 1px solid #e4e7ed;
+                border-bottom: none;
+                background: #fafafa;
+                margin-right: 2px;
+                border-radius: 4px 4px 0 0;
+
+                &:hover {
+                  color: #409eff;
+                  background: #fff;
+                }
+
+                &.is-active {
+                  color: #409eff;
+                  background: #fff;
+                  border-color: #409eff;
+                }
+
+                .el-icon-close {
+                  margin-left: 5px;
+                  font-size: 12px;
+
+                  &:hover {
+                    background-color: #c0c4cc;
+                    color: #fff;
+                    border-radius: 50%;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .el-tabs__content {
+        display: none; // 隐藏内容区域,因为我们使用router-view来显示内容
+      }
+    }
+  }
+
+  // 图标样式
+  .el-tabs__item i {
+    margin-right: 5px;
+    font-size: 14px;
+  }
+</style>

+ 135 - 135
src/layout/LayoutColumns/index.vue

@@ -57,162 +57,162 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, watch } from 'vue'
-import { useRoute, useRouter } from 'vue-router'
-import { usePermissionStore } from '@/store/modules/permission'
-import { useSettingStore } from '@/store/modules/setting'
-import Footer from '../components/Footer/index.vue'
-import SubMenu from '../components/SubMenu/SubMenu.vue'
-import TagsView from '../components/TagsView/index.vue'
-const PermissionStore = usePermissionStore()
-const SettingStore = useSettingStore()
-const route = useRoute()
-const router = useRouter()
-import HeaderToolRight from '../components/Header/ToolRight.vue'
-import HeaderToolLeft from '../components/Header/ToolLeft.vue'
-import Main from '../components/Main/index.vue'
-// 获取路由
-const permission_routes = computed(() => PermissionStore.permission_routes)
+  import { ref, computed, watch } from 'vue'
+  import { useRoute, useRouter } from 'vue-router'
+  import { usePermissionStore } from '@/store/modules/permission'
+  import { useSettingStore } from '@/store/modules/setting'
+  import Footer from '../components/Footer/index.vue'
+  import SubMenu from '../components/SubMenu/SubMenu.vue'
+  import TagsView from '../components/TagsView/index.vue'
+  const PermissionStore = usePermissionStore()
+  const SettingStore = useSettingStore()
+  const route = useRoute()
+  const router = useRouter()
+  import HeaderToolRight from '../components/Header/ToolRight.vue'
+  import HeaderToolLeft from '../components/Header/ToolLeft.vue'
+  import Main from '../components/Main/index.vue'
+  // 获取路由
+  const permission_routes = computed(() => PermissionStore.permission_routes)
 
-// 获取路由
-const menusRoutes = computed(() => {
-  return PermissionStore.permission_routes.filter((item) => !item.hidden)
-})
+  // 获取路由
+  const menusRoutes = computed(() => {
+    return PermissionStore.permission_routes.filter((item) => !item.hidden)
+  })
 
-const activeCurrentMenu = ref('')
-// 主题配置
-const themeConfig = computed(() => SettingStore.themeConfig)
-const isCollapse = computed(() => !SettingStore.isCollapse)
-const activeMenu = computed(() => {
-  const { meta, path } = route
-  return path
-})
-const basePath = ref<string>('/')
-const subMenus = ref([])
+  const activeCurrentMenu = ref('')
+  // 主题配置
+  const themeConfig = computed(() => SettingStore.themeConfig)
+  const isCollapse = computed(() => !SettingStore.isCollapse)
+  const activeMenu = computed(() => {
+    const { meta, path } = route
+    return path
+  })
+  const basePath = ref<string>('/')
+  const subMenus = ref([])
 
-watch(
-  () => [route],
-  () => {
-    if (!menusRoutes.value.length) return
-    const [firstMenu] = route.matched
-    activeCurrentMenu.value = firstMenu.path
-    let menuItem = menusRoutes.value.find((item) => firstMenu.path === item.path)
-    if (menuItem && menuItem.children?.length) {
-      subMenus.value = menuItem.children
-    } else {
-      subMenus.value = []
+  watch(
+    () => [route],
+    () => {
+      if (!menusRoutes.value.length) return
+      const [firstMenu] = route.matched
+      activeCurrentMenu.value = firstMenu.path
+      let menuItem = menusRoutes.value.find((item) => firstMenu.path === item.path)
+      if (menuItem && menuItem.children?.length) {
+        subMenus.value = menuItem.children
+      } else {
+        subMenus.value = []
+      }
+      basePath.value = firstMenu.path
+    },
+    {
+      deep: true,
+      immediate: true,
     }
-    basePath.value = firstMenu.path
-  },
-  {
-    deep: true,
-    immediate: true,
-  }
-)
+  )
 
-const handleChangeMenu = (item) => {
-  router.push(item.path)
-}
+  const handleChangeMenu = (item) => {
+    router.push(item.path)
+  }
 </script>
 
 <style lang="scss" scoped>
-.main-columns {
-  display: flex;
-  flex-direction: row !important;
-  height: 100%;
-  width: 100%;
-}
-.layout-columns-aside {
-  flex-shrink: 0;
-  width: 80px;
-  height: 100%;
-  background-color: #304156;
-  .el-scrollbar {
-    height: calc(100% - 55px);
+  .main-columns {
+    display: flex;
+    flex-direction: row !important;
+    height: 100%;
+    width: 100%;
   }
-  .logo {
-    box-sizing: border-box;
-    height: 50px;
-    img {
-      width: 32px;
-      object-fit: contain;
+  .layout-columns-aside {
+    flex-shrink: 0;
+    width: 80px;
+    height: 100%;
+    background-color: #304156;
+    .el-scrollbar {
+      height: calc(100% - 55px);
     }
-  }
-  .menu-wrap {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    .item-menu-wrap {
+    .logo {
+      box-sizing: border-box;
+      height: 50px;
+      img {
+        width: 32px;
+        object-fit: contain;
+      }
+    }
+    .menu-wrap {
       display: flex;
       flex-direction: column;
       align-items: center;
       justify-content: center;
-      height: 70px;
-      width: 70px;
-      cursor: pointer;
-      transition: all 0.3s ease;
-    }
-    .active-menu {
-      background: $primaryColor;
-      border-radius: 5px;
-    }
-    .title {
-      color: #e5eaf3;
-    }
-    .el-icon {
-      color: #e5eaf3;
+      .item-menu-wrap {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 70px;
+        width: 70px;
+        cursor: pointer;
+        transition: all 0.3s ease;
+      }
+      .active-menu {
+        background: $primaryColor;
+        border-radius: 5px;
+      }
+      .title {
+        color: #e5eaf3;
+      }
+      .el-icon {
+        color: #e5eaf3;
+      }
     }
   }
-}
 
-.layout-columns-sub {
-  flex-shrink: 0;
-  width: 200px;
-  box-sizing: border-box;
-  flex-direction: column;
-  overflow: hidden;
-  transition: all 0.3s ease;
-  background: white;
-  border-right: 1px solid #eee;
-  .el-scrollbar {
-    height: calc(100vh - 50px);
-  }
-  .logo {
-    width: 100%;
+  .layout-columns-sub {
+    flex-shrink: 0;
+    width: 200px;
     box-sizing: border-box;
-    height: 50px;
-    border-bottom: 1px solid #eee;
-    span {
-      font-weight: bold;
-      white-space: nowrap;
+    flex-direction: column;
+    overflow: hidden;
+    transition: all 0.3s ease;
+    background: white;
+    border-right: 1px solid #eee;
+    .el-scrollbar {
+      height: calc(100vh - 50px);
+    }
+    .logo {
+      width: 100%;
+      box-sizing: border-box;
+      height: 50px;
+      border-bottom: 1px solid #eee;
+      span {
+        font-weight: bold;
+        white-space: nowrap;
+      }
+    }
+    ::v-deep(.menu-columns) {
+      border-right: none;
     }
   }
-  ::v-deep(.menu-columns) {
-    border-right: none;
-  }
-}
-.container {
-  flex: 1;
-  overflow: hidden;
-  display: flex;
-  flex-direction: column;
-}
-.layout-header {
-  background: white;
-  transition: width 0.28s;
-  flex-shrink: 0;
-  box-sizing: border-box;
-  box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
-  .header-tool {
-    height: 50px;
-    width: 100%;
-    border-bottom: 1px solid #eee;
+  .container {
+    flex: 1;
+    overflow: hidden;
     display: flex;
-    align-items: center;
-    padding: 0 10px 0 0;
+    flex-direction: column;
+  }
+  .layout-header {
+    background: white;
+    transition: width 0.28s;
+    flex-shrink: 0;
     box-sizing: border-box;
-    justify-content: space-between;
+    box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
+    .header-tool {
+      height: 50px;
+      width: 100%;
+      border-bottom: 1px solid #eee;
+      display: flex;
+      align-items: center;
+      padding: 0 10px 0 0;
+      box-sizing: border-box;
+      justify-content: space-between;
+    }
   }
-}
 </style>

+ 4 - 8
src/layout/components/Header/components/Avatar.vue

@@ -26,7 +26,7 @@
 <script lang="ts" setup>
   import { useRouter } from 'vue-router'
   import { ElMessage, ElMessageBox } from 'element-plus'
-  import { computed, ref,getCurrentInstance  } from 'vue'
+  import { computed, ref, inject } from 'vue'
 
   import { useUserStore } from '@/store/modules/user'
   import { useTagsViewStore } from '@/store/modules/tagsView'
@@ -38,10 +38,11 @@
   const UserStore = useUserStore()
   const TagsViewStore = useTagsViewStore()
   const PermissionStore = usePermissionStore()
-  const v3This = getCurrentInstance()
+  const session = inject('session')
 
   const user = computed(() => {
-    return safeGetUser(v3This..Session)
+    console.log(safeGetUser(session), ' thisad')
+    return safeGetUser(session)
   })
 
   const currentRoles = computed({
@@ -59,11 +60,6 @@
     },
   })
 
-  const switchRolesAction = (type: string) => {
-    if (type === currentRoles.value) return
-    currentRoles.value = currentRoles.value === 'admin' ? 'other' : 'admin'
-  }
-
   // 用户信息
   const userInfo = computed(() => UserStore.userInfo)
   const person = ref()

+ 1 - 1
src/main.ts

@@ -33,7 +33,7 @@ import Config from './config'
 //定义mixin
 const mixins = {
   created() {
-    this.Session = SessionStorage
+    // this.Session = SessionStorage
     this.Config = Config
   },
 }

+ 10 - 17
src/routers/index.ts

@@ -5,16 +5,14 @@ import {
   createWebHashHistory,
   Router,
 } from 'vue-router'
-import Layout from '@/layout/index.vue'
+import Home from '@/views/home2/Home.vue'
 // 扩展继承属性
 interface extendRoute {
   hidden?: boolean
 }
-//
-import systemRouter from './modules/system'
 
-// 异步组件
-export const asyncRoutes = [...systemRouter]
+//
+import uCardRoute from './modules/uCard'
 
 /**
  * path ==> 路由路径
@@ -68,18 +66,13 @@ export const constantRoutes: Array<RouteRecordRaw & extendRoute> = [
   },
   {
     path: '/',
-    name: 'layout',
-    component: Layout,
-    redirect: '/home',
-    meta: { title: '首页', icon: 'House' },
-    children: [
-      {
-        path: '/home',
-        component: () => import('@/views/home/index.vue'),
-        name: 'home',
-        meta: { title: '首页', icon: 'House', affix: true, role: ['other'] },
-      },
-    ],
+    name: '',
+    component: Home,
+    redirect: '/card/order',
+    meta: {
+      OnBreadCrumb: false,
+      requiresAuth: true,
+    },
   },
 ]
 

+ 119 - 0
src/routers/modules/uCard.ts

@@ -0,0 +1,119 @@
+// uCard路由
+const uCardRoute = {
+  meta: {
+    OnBreadCrumb: true,
+  },
+  path: 'card',
+  name: 'R-Card',
+  component: () => import(/* webpackChunkName: "cardpage" */ '@/views/page/Page.vue'),
+  children: [
+    {
+      meta: {
+        OnBreadCrumb: true,
+      },
+      path: 'order',
+      name: 'R-CardOrder',
+      component: () => import(/* webpackChunkName: "CardOrder" */ '@/views/card/CardOrder.vue'),
+    },
+    //
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'kycAuth',
+    //     name: 'R-KycAuth',
+    //     component: () => import(/* webpackChunkName: "KycAuth" */ '@/views/card/KycAuth.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'type',
+    //     name: 'R-CardType',
+    //     component: () => import(/* webpackChunkName: "CardType" */ '@/views/card/CardType.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'operate',
+    //     name: 'R-CardOperate',
+    //     component: () => import(/* webpackChunkName: "CardOperate" */ '@/views/card/CardOperate.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'userOrder',
+    //     name: 'R-UserOrder',
+    //     component: () => import(/* webpackChunkName: "UserOrder" */ '@/views/card/UserOrder.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'userRights',
+    //     name: 'R-UserRights',
+    //     component: () => import(/* webpackChunkName: "UserRights" */ '@/views/card/UserRights.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'config',
+    //     name: 'R-CardConfig',
+    //     component: () => import(/* webpackChunkName: "CardConfig" */ '@/views/card/CardConfig.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'idTypeConfig',
+    //     name: 'R-IdTypeConfig',
+    //     component: () =>
+    //       import(/* webpackChunkName: "IdTypeConfig" */ '@/views/card/IdTypeConfig.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'business',
+    //     name: 'R-Business',
+    //     component: () => import(/* webpackChunkName: "Business" */ '@/views/card/Business.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'virtual',
+    //     name: 'R-VirtualCard',
+    //     component: () => import(/* webpackChunkName: "VirtualCard" */ '@/views/card/VirtualCard.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'transactions',
+    //     name: 'R-Transactions',
+    //     component: () =>
+    //       import(/* webpackChunkName: "Transactions" */ '@/views/card/Transactions.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'recharge',
+    //     name: 'R-Recharge',
+    //     component: () => import(/* webpackChunkName: "Recharge" */ '@/views/card/Recharge.vue'),
+    //   },
+    //   {
+    //     meta: {
+    //       OnBreadCrumb: true,
+    //     },
+    //     path: 'detail/:cardNumber',
+    //     name: 'R-CardDetail',
+    //     component: () => import(/* webpackChunkName: "CardDetail" */ '@/views/card/CardDetail.vue'),
+    //   },
+  ],
+}
+export default uCardRoute

+ 17 - 0
src/service/apply.ts

@@ -0,0 +1,17 @@
+import Service from '../lib/service.js'
+import axios from 'axios'
+import Config from '../config'
+
+class ApplyService extends Service {
+  constructor() {
+    super()
+    axios.defaults.baseURL = Config.Host85
+  }
+
+  //根据条件查看拒绝列表-用于下拉和选择展示理由
+  async reasonsRefusalList(params = {}) {
+    return await this.post('/reasons/refusal/list', params)
+  }
+}
+
+export default new ApplyService()

+ 24 - 0
src/service/customer.ts

@@ -0,0 +1,24 @@
+import Service from '../lib/service.js'
+import axios from 'axios'
+import Config from '../config'
+
+class CustomerService extends Service {
+  constructor() {
+    super()
+    axios.defaults.baseURL = Config.Host85
+  }
+
+  //cid详细信息real
+  async cidRealSingle(params = {}) {
+    return await this.post('/custom/search/real/single', params)
+  }
+  //真实用户列表all
+  async realCustomerListAll(params = {}) {
+    return await this.post('/custom/login/search/custom/list', params)
+  }
+  //反审核
+  async customFileApproveDe(params = {}) {
+    return await this.post('/custom/file/de/approve', params)
+  }
+}
+export default new CustomerService()

+ 25 - 0
src/service/ucard.ts

@@ -0,0 +1,25 @@
+import Service from '../lib/service.js'
+import axios from 'axios'
+import Config from '../config'
+
+class UCardService extends Service {
+  constructor() {
+    super()
+    axios.defaults.baseURL = Config.Host85
+  }
+
+  // 申请详情
+  async applyDetails(params = {}) {
+    return await this.post('/wasabi/card/apply/details', params)
+  }
+  // 查询开卡进度
+  async ucardApplyProgress(params = {}) {
+    return await this.post('/wasabi/card/apply/progress', params)
+  }
+  // 获取卡片申请列表
+  async applyList(params = {}) {
+    return await this.post('/wasabi/card/apply/page', params)
+  }
+}
+
+export default new UCardService()

+ 651 - 0
src/views/card/CardOrder/index.vue

@@ -0,0 +1,651 @@
+<template>
+  <div
+    id="review_Email"
+    v-loading="pictLoading"
+    class="view"
+    element-loading-background="rgba(43, 48, 67, 0.65)"
+    element-loading-spinner="el-icon-loading"
+  >
+    <div class="crm_search">
+      <el-form ref="form" label-position="" :model="search" label-width="">
+        <el-row>
+          <el-col :span="24" :md="24" :lg="24">
+            <el-form-item>
+              <el-select
+                v-model="search.tag"
+                class="crm_search_down crm-border-radius-no"
+                size="small"
+                :placeholder="$t('Placeholder.Choose')"
+              >
+                <el-option :label="$t('Label.CidAccount')" :value="1"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item2')" :value="2"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item3')" :value="3"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-input
+                v-if="search.tag == 1"
+                v-model.trim="search.cId"
+                class="crm-border-left-no crm-border-radius-no"
+                size="small"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 2"
+                v-model.trim="search.mobile"
+                class="crm-border-left-no crm-border-radius-no"
+                size="small"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 3"
+                v-model.trim="search.email"
+                class="crm-border-left-no crm-border-radius-no"
+                size="small"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.tradeStatus"
+                class="crm-border-radius-no"
+                clearable
+                size="small"
+                :placeholder="$t('Ucard.CardOrder.item8')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.CardOrder.t5')" :value="1"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t8')" :value="2"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t9')" :value="3"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t11')" :value="4"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t12')" :value="5"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t13')" :value="6"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.approveStatus"
+                class="crm-border-radius-no"
+                clearable
+                size="small"
+                :placeholder="$t('R-Review-Status')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.UserOrder.t11')" value="1"></el-option>
+                <el-option :label="$t('Ucard.UserOrder.t12')" value="2"></el-option>
+                <el-option :label="$t('Ucard.UserOrder.t13')" value="3"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-select
+                v-model="search.status"
+                class="crm-border-radius-no"
+                clearable
+                size="small"
+                :placeholder="$t('card.Info.s41')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.CardOrder.t10')" value="wait_process"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t5')" value="processing"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t6')" value="success"></el-option>
+                <el-option :label="$t('card.New2.p3')" value="cancel"></el-option>
+                <el-option :label="$t('Ucard.CardOrder.t7')" value="fail"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button
+                class="crm-border-radius-no crm-border-left-no"
+                size="small"
+                icon="el-icon-search"
+                @click="toSearch"
+              ></el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item>
+          <el-button
+            v-if="display['R-CardOrder-Export'] && display['R-CardOrder-Export'].show"
+            type="primary"
+            style="margin-left: 8px"
+            size="small"
+            @click="exportAgents"
+            ><span>
+              {{ $t('Btn.Export') }}
+            </span></el-button
+          >
+        </el-form-item>
+      </el-form>
+
+      <div class="card-mock-demo" style="margin: 30px 0">
+        <el-table :data="mock_tableData" stripe style="margin-top: 20px; width: 100%">
+          <el-table-column prop="" align="left" :label="$i18n.t('Label.CidAccount')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.cId && display['R-CardOrder-Btn1'].show"
+                class="crm-text-underline"
+                @click="accountOpen(scope.row.cId)"
+                >{{ scope.row.cId || '--' }}</span
+              >
+              <span v-else>{{ scope.row.cId || '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$i18n.t('Label.Name')">
+            <template #default="scope">
+              <span v-if="scope.row.firstName">{{ scope.row.firstName + ' ' }}</span>
+              <span v-if="scope.row.middle">{{ scope.row.middle + ' ' }}</span>
+              <span v-if="scope.row.lastName">{{ scope.row.lastName }}</span>
+              <span v-if="!scope.row.firstName && !scope.row.lastName && !scope.row.middle">{{
+                '--'
+              }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$i18n.t('Label.Email')">
+            <template #default="scope">
+              {{ scope.row.email || '--' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="mobile" align="left" :label="$t('Ucard.KycAuth.item2')">
+            <template #default="scope"> {{ scope.row.areaCode }} {{ scope.row.mobile }} </template>
+          </el-table-column>
+          <el-table-column prop="cardName" align="left" :label="$t('card.Form.f49')">
+          </el-table-column>
+          <el-table-column prop="type" align="left" :label="$t('Ucard.KycAuth.item1')">
+          </el-table-column>
+          <el-table-column prop="cardNumber" align="left" :label="$t('card.Form.f24')">
+            <template #default="scope">
+              <span>{{ scope.row.cardNumber || '--' }}</span>
+            </template>
+          </el-table-column>
+
+          <el-table-column prop="tradeStatus" align="left" :label="$t('Ucard.CardOrder.item8')">
+            <template #default="scope">
+              <template v-if="scope.row.tradeType == '1'">
+                <span v-if="scope.row.tradeStatus == '2'" class="state crm_state_blue">
+                  {{ $t('Ucard.CardOrder.t8') }}
+                </span>
+                <span v-else-if="scope.row.tradeStatus == '3'" class="state crm_state_gray">
+                  {{ $t('Ucard.CardOrder.t9') }}
+                </span>
+                <span v-else class="state crm_state_yellow">
+                  {{ $t('Ucard.CardOrder.t5') }}
+                </span>
+              </template>
+
+              <template v-if="scope.row.tradeType == '2'">
+                <span v-if="scope.row.tradeStatus == '2'" class="state crm_state_blue">
+                  {{ $t('Ucard.CardOrder.t12') }}
+                </span>
+                <span
+                  v-else-if="scope.row.tradeStatus == '3'"
+                  v-t="'Ucard.CardOrder.t13'"
+                  class="state crm_state_gray"
+                >
+                  {{ $t('Ucard.CardOrder.t13') }}
+                </span>
+                <span v-else class="state crm_state_yellow">
+                  {{ $t('Ucard.CardOrder.t11') }}
+                </span>
+              </template>
+            </template>
+          </el-table-column>
+          <el-table-column prop="approveStatus" align="left" :label="$t('R-Review-Status')">
+            <template #default="scope">
+              <span v-if="scope.row.approveStatus == '2'" class="state crm_state_blue">
+                {{ $t('Ucard.UserOrder.t12') }}
+              </span>
+              <span v-else-if="scope.row.approveStatus == '3'" class="state crm_state_gray">
+                {{ $t('Ucard.UserOrder.t13') }}
+              </span>
+              <span v-else class="state crm_state_yellow">
+                {{ $t('Ucard.UserOrder.t11') }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="status" align="left" :label="$t('card.Info.s41')">
+            <template #default="scope">
+              <span v-if="scope.row.status === 'success'" class="state crm_state_blue">
+                {{ $t('Ucard.UserOrder.t6') }}
+              </span>
+              <span v-else-if="scope.row.status === 'fail'" class="state crm_state_gray">
+                {{ $t('Ucard.UserOrder.t7') }}
+              </span>
+              <span v-else-if="scope.row.status === 'processing'" class="state crm_state_yellow">
+                {{ $t('Ucard.UserOrder.t5') }}
+              </span>
+              <span
+                v-else-if="scope.row.status === 'cancel'"
+                v-t="'card.New2.p3'"
+                class="state crm_state_red"
+              >
+                {{ $t('Ucard.UserOrder.t12') }}
+              </span>
+              <span v-else v-t="'Ucard.CardOrder.t10'" class="state crm_state_orange">
+                {{ $t('Ucard.UserOrder.t12') }}
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="note" align="left" :label="$t('Ucard.CardOrder.item9')">
+            <template #default="scope">
+              <template v-if="scope.row.approveDesc">
+                <span v-if="reasons[scope.row.approveDesc]">{{
+                  Session.Get('lang') == 'cn'
+                    ? reasons[scope.row.approveDesc].content
+                    : reasons[scope.row.approveDesc].enContent
+                }}</span>
+                <span v-else>{{ scope.row.approveDesc || '--' }}</span>
+              </template>
+              <span v-else>{{ scope.row.note || '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="center" :label="$i18n.t('Label.Action')">
+            <template #default="scope">
+              <el-dropdown trigger="click" @command="handleCommand">
+                <span class="el-dropdown-link crm-cursor">
+                  <i style="font-weight: bold; font-size: 20px" class="iconfont iconcaidan"></i>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      v-if="display['R-CardOrder-Btn1'].show"
+                      :command="{ type: 1, row: scope.row }"
+                    >
+                      <i class="el-icon-s-operation"></i
+                      ><span v-t="'R-CardOrder-Btn1'">
+                        {{ $t('R-CardOrder-Btn1') }}
+                      </span>
+                    </el-dropdown-item>
+                    <el-dropdown-item
+                      v-if="
+                        display['R-CardOrder-Btn2'].show &&
+                        (scope.row.status == 'wait_process' ||
+                          scope.row.status == 'processing' ||
+                          scope.row.status == null) &&
+                        scope.row.tradeStatus == '2' &&
+                        scope.row.approveStatus == '2'
+                      "
+                      :command="{ type: 2, row: scope.row }"
+                    >
+                      <i class="el-icon-s-operation"></i
+                      ><span>
+                        {{ $t('R-CardOrder-Btn2') }}
+                      </span>
+                    </el-dropdown-item>
+                    <el-dropdown-item
+                      v-if="
+                        display['R-CardOrder-Btn3'].show &&
+                        scope.row.approveStatus == '1' &&
+                        scope.row.tradeStatus == '2'
+                      "
+                      :command="{ type: 3, row: scope.row }"
+                    >
+                      <i class="el-icon-s-operation"></i
+                      ><span>
+                        {{ $t('R-CardOrder-Btn3') }}
+                      </span>
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <div v-if="pagerInfo.rowTotal" class="crm_pagination">
+      <div class="crm_page_total">
+        <span>
+          {{ $t('Page.total.item1') }}
+        </span>
+        <span>{{ pagerInfo.rowTotal }}</span>
+        <span>
+          {{ $t('Page.total.item2') }}
+        </span>
+      </div>
+      <el-pagination
+        class="page"
+        background
+        layout="sizes, prev, pager, next"
+        :total="pagerInfo.rowTotal"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pagerInfo.row"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
+      >
+      </el-pagination>
+    </div>
+    <detailed-info-cid
+      :dialog-info-cid="dialogInfoCid"
+      :form-info="formInfo"
+      :is-trading="true"
+      @close="close"
+    >
+    </detailed-info-cid>
+    <div v-if="dialogInfoCid" class="crm_verified_info_mask" @click="closeDia"></div>
+    <ViewCardSingle
+      :dialog-info-trading-single="dialogInfoTradingSingle"
+      :form-list="formList"
+      :editor-type="editorType"
+      @close-single="closeSingle"
+    >
+    </ViewCardSingle>
+    <div v-if="dialogInfoTradingSingle" class="crm_verified_info_mask" @click="closeSingle"></div>
+    <trading-info-add
+      :dialog-info-trading-add="dialogInfoTradingAdd"
+      :editor="editor"
+      :add-type="addType"
+      :my-info="myInfo"
+      :form-list="formList"
+      @confirm-to-reload="confirmToReload"
+      @close-add="closeAdd"
+    ></trading-info-add>
+    <div v-if="dialogInfoTradingAdd" class="crm_verified_info_mask" @click="closeDiaAdd"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, computed, watch, onMounted, nextTick, inject } from 'vue'
+  import { useRouter } from 'vue-router'
+  import Service from '@/service/ucard'
+  import Config from '@/config/index'
+  import Service1 from '@/service/customer'
+  import Service2 from '@/service/apply'
+  import DetailedInfoCid from '@/views/global/DetailedInfoCid'
+  import TradingInfoAdd from '@/views/global/VirtualCard'
+  import ViewCardSingle from '@/views/global/ViewCardSingle'
+  import { exportExcel } from '@/utils/export'
+  // 混入
+  const Session = inject('session')
+  const { Code } = Config
+  const router = useRouter()
+
+  // 响应式数据
+  const reasons = ref({})
+  const pictLoading = ref(false)
+  const dialogInfoTradingSingle = ref(false)
+  const dialogInfoTradingAdd = ref(false)
+  const search = reactive({
+    tag: 1,
+    mobile: '',
+    cId: '',
+    email: '',
+    status: '',
+  })
+  const editor = ref('')
+  const addType = ref('')
+  const myInfo = ref({})
+  const dInfo = ref({})
+  const formList = ref({})
+  const editorType = ref(1)
+  const mock_tableData = ref([])
+  const pagerInfo = reactive({ row: 10, current: 1, pageTotal: 0, rowTotal: 0 })
+  const dialogInfoCid = ref(false)
+  const formInfo = ref({})
+
+  // 计算属性
+  const display = computed(() => {
+    try {
+      return JSON.parse(Session.Get('display', true) || '{}')
+    } catch (error) {
+      console.error('Error parsing display data:', error)
+      return {}
+    }
+  })
+
+  const user = computed(() => {
+    try {
+      return JSON.parse(Session.Get('user', true) || '{}')
+    } catch (error) {
+      console.error('Error parsing user data:', error)
+      return {}
+    }
+  })
+
+  // 监听器
+  watch(
+    () => search.tag,
+    () => {
+      search.mobile = ''
+      search.email = ''
+      search.cId = ''
+      search.status = ''
+    }
+  )
+
+  // 生命周期
+  onMounted(() => {
+    searchReasons()
+    searchFunc()
+  })
+
+  // 导出
+  const exportAgents = async () => {
+    exportExcel('/wasabi/card/apply/list/export', { ...search }, 'Card_Applications')
+  }
+
+  const getDetail = async (row) => {
+    try {
+      const res = await Service.applyDetails({
+        id: row.id,
+      })
+      dInfo.value = res.data
+    } catch (error) {
+      ElMessage.error('搜索失败')
+    }
+  }
+
+  // 详细信息cid
+  const accountOpen = (cId) => {
+    router.push({ name: 'R-CustomerDetail', params: { cId: cId } })
+  }
+
+  const searchSingleCid = async (cId) => {
+    const res = await Service1.cidRealSingle({
+      id: cId,
+    })
+    if (res.code == Code.StatusOK) {
+      if (editor.value) {
+        formList.value = res.data
+        dialogInfoTradingAdd.value = true
+      } else {
+        formInfo.value = res.data
+        searchRealAll(cId)
+      }
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  // 获取cid下的真实账户信息
+  const searchRealAll = async (cId) => {
+    const res = await Service1.realCustomerListAll({
+      cId: cId,
+      page: {
+        current: 1,
+        row: 1,
+      },
+    })
+    if (res.code == Code.StatusOK) {
+      formInfo.value.realList = res.data
+      dialogInfoCid.value = true
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  // 详细信息关闭cid/pibno
+  const closeDia = () => {
+    dialogInfoCid.value = false
+  }
+
+  const close = (val) => {
+    dialogInfoCid.value = val
+  }
+
+  const closeAdd = (val) => {
+    formList.value = {}
+    dialogInfoTradingAdd.value = val
+  }
+
+  const closeDiaAdd = () => {
+    formList.value = {}
+    dialogInfoTradingAdd.value = false
+  }
+
+  const confirmToReload = () => {
+    closeDiaAdd()
+    searchFunc()
+  }
+
+  // 详情
+  const closeSingle = () => {
+    dialogInfoTradingSingle.value = false
+  }
+
+  // 点击操作的回调
+  const handleCommand = async (command) => {
+    switch (command.type) {
+      case 1:
+        await getDetail(command.row)
+        await nextTick(() => {
+          formList.value = { ...command.row, ...dInfo.value }
+          dialogInfoTradingSingle.value = true
+        })
+        break
+      case 2:
+        if (!(command.row.status == 'success' || command.row.status == 'fail')) {
+          updateCardTypes(command.row)
+        } else {
+          ElMessage.success('操作成功')
+        }
+        break
+      case 3:
+        addType.value = 4
+        dialogInfoTradingAdd.value = true
+        formList.value = { ...command.row }
+        break
+      default:
+        break
+    }
+  }
+
+  // 获取原因列表
+  const searchReasons = async () => {
+    const res = await Service2.reasonsRefusalList({ type: 15 })
+    if (res.code == Code.StatusOK) {
+      if (res.data == null) {
+        reasons.value = {}
+      } else {
+        reasons.value = res.data
+      }
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  const toSearch = () => {
+    pagerInfo.current = 1
+    searchFunc()
+  }
+
+  // 更新
+  const updateCardTypes = async (row) => {
+    const res = await Service.ucardApplyProgress({
+      holderMerchantOrderNo: row.holderMerchantOrderNo,
+      id: row.id,
+    })
+    if (res.code == Code.StatusOK) {
+      ElMessage.success('搜索成功')
+      searchFunc()
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  // 列表
+  const searchFunc = async () => {
+    pictLoading.value = true
+    if (!display.value['R-CardOrder-Search']?.show) {
+      ElMessage.warning('无显示权限')
+      pictLoading.value = false
+      return
+    }
+
+    try {
+      const res = await Service.applyList({
+        ...search,
+        page: {
+          current: pagerInfo.current,
+          row: pagerInfo.row,
+        },
+      })
+      if (res.code == Code.StatusOK) {
+        mock_tableData.value = res.data
+        if (res.page != null) {
+          pagerInfo.rowTotal = res.page.rowTotal
+          pagerInfo.pageTotal = res.page.pageTotal
+        } else {
+          pagerInfo.rowTotal = 0
+        }
+        ElMessage.success('搜索成功')
+      } else {
+        ElMessage.error(res.msg)
+      }
+    } catch (error) {
+      ElMessage.error('搜索失败')
+    } finally {
+      pictLoading.value = false
+    }
+  }
+
+  const handleSizeChange = (val) => {
+    pagerInfo.row = val
+    searchFunc()
+  }
+
+  const handleCurrentChange = (val) => {
+    pagerInfo.current = val
+    searchFunc()
+  }
+</script>
+
+<style scoped lang="scss">
+  #review_Email {
+    .crm_search {
+      .search_action_btn {
+        .delete {
+          background-color: #a1a1a1;
+        }
+
+        .delete.active {
+          background-color: #368fec;
+        }
+      }
+    }
+
+    .el-table .state {
+      display: inline-block;
+      min-width: 80px;
+      max-width: 150px;
+      box-sizing: border-box;
+      line-height: 1.5;
+      border-radius: 2px;
+      padding: 2px 10px;
+      color: #ffffff;
+    }
+  }
+</style>
+<style lang="scss">
+  #review_Email {
+    .dialog_header_w {
+      .crm_search_down {
+        width: 400px;
+      }
+    }
+  }
+</style>

+ 127 - 0
src/views/components/DetailedInfoCid/index.scss

@@ -0,0 +1,127 @@
+.btn-copy {
+  margin-left: 5px;
+  padding: 4px 8px;
+  border-right: 4px;
+  background-color: rgba(84, 129, 214, 1);
+  color: #ffffff;
+  cursor: pointer;
+  transition: all 0.25s linear;
+  font-size: 12px;
+  min-width: 30px;
+
+  &:hover {
+    background-color: rgba(84, 129, 214, 0.8);
+  }
+}
+
+#GlobalDetailedInfoCid.InfoBox {
+  width: 450px;
+  height: 100%;
+  padding: 15px 0;
+  border-radius: 2px;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  box-shadow: 0px 3px 5px 0px rgba(49, 49, 49, 0.35);
+  position: fixed;
+  background-color: #ffffff;
+  z-index: 99;
+  overflow: hidden;
+  overflow-y: auto;
+  top: 0;
+  right: -455px;
+  -webkit-transition: all 0.6s;
+  -moz-transition: all 0.6s;
+  -o-transition: all 0.6s;
+  transition: all 0.6s;
+
+  .header {
+    text-align: left;
+    height: 25px;
+    line-height: 25px;
+    margin: 0 20px 10px;
+    font-size: 14px;
+    display: flex;
+    justify-content: space-between;
+    .title {
+      font-weight: bold;
+    }
+    i.el-icon-close {
+      text-align: right !important;
+      font-weight: bold;
+      margin-right: 7px;
+    }
+  }
+
+  .btn {
+    padding: 6px 8px;
+    background-color: #368fec;
+    color: #ffffff;
+    border-radius: 2px;
+    text-align: center;
+    min-width: 50px;
+  }
+
+  .Identity {
+    margin: 20px 0;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .title {
+      color: #2b3043;
+      font-weight: bold;
+    }
+    .line {
+      flex: 1;
+      box-sizing: border-box;
+      margin: 0 10px;
+      border-bottom: 1px dashed #2b3043;
+    }
+  }
+  .Identity-img {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    flex-wrap: wrap;
+    margin-bottom: 10px;
+    .line {
+      flex: 1;
+      box-sizing: border-box;
+      margin: 0 10px;
+      border-bottom: 1px dashed #2b3043;
+    }
+    .id-img {
+      width: 150px;
+      height: 80px;
+      line-height: 80px;
+      border: 1px dashed #6b7188;
+      position: relative;
+      font-size: 24px;
+      .active {
+        display: none;
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+        background-color: rgba(43, 48, 67, 0.65);
+        color: #ffffff;
+        i {
+          margin: 0 5px;
+          font-size: 20px;
+        }
+      }
+    }
+    .id-img:hover {
+      .active {
+        display: block;
+      }
+    }
+  }
+}
+
+#GlobalDetailedInfoCid.InfoBox.active {
+  right: 0;
+}
+#GlobalDetailedInfoCid.InfoBox.active.active1 {
+  right: 450px;
+}

+ 1049 - 0
src/views/components/DetailedInfoCid/index.vue

@@ -0,0 +1,1049 @@
+<template>
+  <div class="InfoBox" id="GlobalDetailedInfoCid" :class="{ active: dialogInfoCid, active1: type }">
+    <div class="header">
+      <div>
+        <span class="title">{{ $t('Label.CidAccount') }}</span> :<span>{{ form.cId }}</span>
+        -
+        <span v-if="form.firstName">{{ form.firstName + ' ' }}</span>
+        <span v-if="form.middle">{{ form.middle + ' ' }}</span>
+        <span v-if="form.lastName">{{ form.lastName }}</span>
+      </div>
+      <span class="close crm-cursor" @click="close"><i class="el-icon-close"></i></span>
+    </div>
+    <el-form ref="formRef" :model="form" label-width="150PX">
+      <el-collapse class="crm_collapse_info" v-model="activeNames">
+        <el-collapse-item :title="$t('Apply_info.UserInfo.Title')" name="1">
+          <el-form-item :label="$t('Apply_info.UserInfo.CID')">
+            {{ form.cId || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.UserInfo.Email')">
+            {{ form.email || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.UserInfo.Registration')">
+            {{ form.addTime || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.UserInfo.IP')">
+            {{ form.addIp || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Raffle.FundModification.WalletBalance')">
+            {{ form.balance || '0' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.UserInfo.LoginState')">
+            <el-switch
+              class="crm_switch"
+              v-model="form.valid"
+              disabled
+              :active-value="1"
+              :inactive-value="0"
+              :active-text="$t('Btn.Yes')"
+              :inactive-text="$t('Btn.No')"
+              active-color="#368FEC"
+              inactive-color="#EB3F57"
+            >
+            </el-switch>
+          </el-form-item>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Label.Ib')" name="7" v-if="form.isAgent">
+          <el-form-item :label="$t('Label.AgentNumber')">
+            {{ form.ibNo || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.AgentLevel')">
+            {{
+              Session.Get('lang') == 'cn'
+                ? form.agentLevelName || '--'
+                : form.agentLevelEnName || '--'
+            }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.CommissionBalance')">
+            {{ form.agentBalance || '0' }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.Unresolved')">
+            {{ form.unresolved || '0' }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.Frozen')">
+            {{ form.frozen || '0' }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.AllCommission')">
+            {{ form.allCommission || '0' }}
+          </el-form-item>
+          <el-form-item :label="$t('Dashboard.Profile.AgentShareLink')">
+            <span class="btn-copy" @click="CopyShareLink">{{
+              $t('Dashboard.Profile.ConsumerShareLinks')
+            }}</span>
+          </el-form-item>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Apply_info.TradingAccount.Title')" name="10" v-if="isTrading">
+          <el-card class="box-card" v-for="(item, index) in form.realList" :key="index">
+            <template #header>
+              <div class="clearfix crm-cursor" @click="cardTab(index)">
+                <span>
+                  <span>{{ index + 1 }}. </span
+                  ><span>{{ $t('Apply_info.TradingAccount.Account') }}</span> {{ item.login }} -
+                  <span v-if="item.type == 1">{{ $t('Web_info.Classic') }}</span>
+                  <span v-if="item.type == 2">{{ $t('Web_info.Senior') }}</span>
+                  <span v-if="item.type == 3">{{ $t('Web_info.Institutions') }}</span>
+                  <span v-if="item.type == 5">{{ $t('Web_info.Speed') }}</span>
+                  <span v-if="item.type == 6">{{ $t('Web_info.NewSpeed') }}</span>
+                  <span v-if="item.type == 7">{{ $t('Web_info.StandardAccount') }}</span>
+                  <span v-if="item.type == 8">{{ $t('Web_info.CentAccount') }}</span>
+                </span>
+                <span>
+                  <i v-if="cardIndex == index" class="el-icon-caret-bottom"></i>
+                  <i v-if="cardIndex != index" class="el-icon-caret-right"></i>
+                </span>
+              </div>
+            </template>
+            <el-collapse-transition>
+              <div class="text item" v-if="cardIndex == index">
+                <el-form-item :label="$t('Apply_info.TradingAccount.Trading')">
+                  {{ item.login || '--' }}
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.Type')">
+                  <span v-if="item.type == 1">{{ $t('Web_info.Classic') }}</span>
+                  <span v-if="item.type == 2">{{ $t('Web_info.Senior') }}</span>
+                  <span v-if="item.type == 3">{{ $t('Web_info.Institutions') }}</span>
+                  <span v-if="item.type == 5">{{ $t('Web_info.Speed') }}</span>
+                  <span v-if="item.type == 6">{{ $t('Web_info.NewSpeed') }}</span>
+                  <span v-if="item.type == 7">{{ $t('Web_info.StandardAccount') }}</span>
+                  <span v-if="item.type == 8">{{ $t('Web_info.CentAccount') }}</span>
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.Leverage')">
+                  <span v-if="item.leverage">1:</span>{{ item.leverage || '0' }}
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.OutsideCommission')">
+                  {{ item.commission || '0' }}
+                </el-form-item>
+                <el-form-item
+                  :label="$t('Apply_info.TradingAccount.Group')"
+                  v-if="user.departmentId != 1"
+                >
+                  {{ item.groupCode || '--' }}
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.Currency')">
+                  {{ item.currency || '--' }}
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.Time')">
+                  {{ item.addTime || '--' }}
+                </el-form-item>
+                <el-form-item :label="$t('Apply_info.TradingAccount.State')">
+                  <el-switch
+                    class="crm_switch"
+                    disabled
+                    v-model="item.valid"
+                    :active-value="1"
+                    :inactive-value="0"
+                    :active-text="$t('Btn.Yes')"
+                    :inactive-text="$t('Btn.No')"
+                    active-color="#368FEC"
+                    inactive-color="#EB3F57"
+                  >
+                  </el-switch>
+                </el-form-item>
+              </div>
+            </el-collapse-transition>
+          </el-card>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Apply_info.BasicInfo.Title')" name="2">
+          <el-form-item :label="$t('Label.CustomerType')">
+            <span v-if="form.customType == 2">{{ $t('Label.CustomerType2') }}</span>
+            <span v-if="form.customType == 1">{{ $t('Label.CustomerType1') }}</span>
+            <span v-if="!form.customType">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Label.CompanyName')" v-if="form.customType == 2">
+            {{ form.companyName || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Label.CorporationName')" v-if="form.customType == 2">
+            <span v-if="form.firstName">{{ form.firstName + ' ' }}</span>
+            <span v-if="form.middle">{{ form.middle + ' ' }}</span>
+            <span v-if="form.lastName">{{ form.lastName }}</span>
+            <span v-if="!form.firstName && !form.lastName && !form.middle">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.Name')" v-else>
+            <span v-if="form.firstName">{{ form.firstName + ' ' }}</span>
+            <span v-if="form.middle">{{ form.middle + ' ' }}</span>
+            <span v-if="form.lastName">{{ form.lastName }}</span>
+            <span v-if="!form.firstName && !form.lastName && !form.middle">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('system_info.Label.EnglishNames')">
+            {{ form.nameEn || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.Phone')" v-if="user.departmentId != 1">
+            {{ form.phone || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.ID')" v-if="user.departmentId != 1">
+            {{ form.identity || '--' }}
+          </el-form-item>
+          <el-form-item
+            :label="$t('Apply_info.BasicInfo.IdentityNumber')"
+            v-if="user.departmentId != 1"
+          >
+            {{ form.identityNumber || '--' }}
+          </el-form-item>
+          <el-form-item
+            :label="$t('Apply_info.BasicInfo.KycIdNumber')"
+            v-if="user.departmentId != 1"
+          >
+            {{ form.kycIdNumber || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.KycNumber')" v-if="user.departmentId != 1">
+            {{ form.kycNumber || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.KycName')" v-if="user.departmentId != 1">
+            {{ form.kycName || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.Birthday')">
+            <span v-if="form.birth">{{ form.birth.split(' ')[0] }}</span>
+            <span v-if="!form.birth">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.BasicInfo.Gender')">
+            <span v-if="form.gender == '1'">{{ $t('Apply_info.BasicInfo.Male') }}</span>
+            <span v-if="form.gender == '2'">{{ $t('Apply_info.BasicInfo.Female') }}</span>
+            <span v-if="!form.gender">{{ '--' }}</span>
+          </el-form-item>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Apply_info.AddressInfo.Title')" name="3">
+          <el-form-item :label="$t('Apply_info.AddressInfo.Lang')">
+            {{ form.lang == 'cn' ? '中文' : 'English' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.Country')">
+            {{ Session.Get('lang') == 'cn' ? form.countryName : form.countryEnName }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.Region')">
+            {{ form.state || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.City')">
+            {{ form.city || '--' }}
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.Address')">
+            <span v-if="form.addressLines">{{
+              form.addressLines.length ? form.addressLines[0] : '--'
+            }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.standbyAddress')">
+            <span v-if="form.addressLines">{{
+              form.addressLines.length == 2 ? form.addressLines[1] : '--'
+            }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.AddressInfo.zipCode')">
+            <span>{{ form.zipCode || '--' }}</span>
+          </el-form-item>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Apply_info.FinancialBack.Title')" name="4">
+          <el-form-item :label="$t('Apply_info.FinancialBack.EducationBackground')">
+            <span v-if="form.level == 1">{{ $t('Apply_info.FinancialBack.item11') }}</span>
+            <span v-if="form.level == 2">{{ $t('Apply_info.FinancialBack.item12') }}</span>
+            <span v-if="form.level == 3">{{ $t('Apply_info.FinancialBack.item13') }}</span>
+            <span v-if="form.level == 4">{{ $t('Apply_info.FinancialBack.item14') }}</span>
+            <span v-if="form.level == 5">{{ $t('Apply_info.FinancialBack.item15') }}</span>
+            <span v-if="!form.level">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.FinancialBack.InaugurationSituation')">
+            <span v-if="form.employmentStatus == 1">{{
+              $t('Apply_info.FinancialBack.item21')
+            }}</span>
+            <span v-if="form.employmentStatus == 2">{{
+              $t('Apply_info.FinancialBack.item22')
+            }}</span>
+            <span v-if="form.employmentStatus == 3">{{
+              $t('Apply_info.FinancialBack.item23')
+            }}</span>
+            <span v-if="form.employmentStatus == 4">{{
+              $t('Apply_info.FinancialBack.item24')
+            }}</span>
+            <span v-if="form.employmentStatus == 5">{{
+              $t('Apply_info.FinancialBack.item25')
+            }}</span>
+            <span v-if="!form.employmentStatus">{{ '--' }}</span>
+          </el-form-item>
+          <!--          <el-form-item :label="$t('Apply_info.FinancialBack.Industry')">-->
+          <!--            {{form.occupation || '&#45;&#45;'}}-->
+          <!--          </el-form-item>-->
+          <el-form-item :label="$t('Apply_info.FinancialBack.TradingPurposes')">
+            <span v-if="form.tradingObjectives == 1">{{
+              $t('Apply_info.FinancialBack.item31')
+            }}</span>
+            <span v-if="form.tradingObjectives == 2">{{
+              $t('Apply_info.FinancialBack.item32')
+            }}</span>
+            <span v-if="form.tradingObjectives == 3">{{
+              $t('Apply_info.FinancialBack.item33')
+            }}</span>
+            <span v-if="form.tradingObjectives == 4">{{
+              $t('Apply_info.FinancialBack.item34')
+            }}</span>
+            <span v-if="!form.tradingObjectives">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.FinancialBack.SourcesFunding')">
+            <span v-if="form.sourceFunding == 1">{{ $t('Apply_info.FinancialBack.item41') }}</span>
+            <span v-if="form.sourceFunding == 2">{{ $t('Apply_info.FinancialBack.item42') }}</span>
+            <span v-if="form.sourceFunding == 3">{{ $t('Apply_info.FinancialBack.item43') }}</span>
+            <span v-if="form.sourceFunding == 4">{{ $t('Apply_info.FinancialBack.item44') }}</span>
+            <span v-if="form.sourceFunding == 5">{{ $t('Apply_info.FinancialBack.item45') }}</span>
+            <span v-if="form.sourceFunding == 6">{{ $t('Apply_info.FinancialBack.item46') }}</span>
+            <span v-if="!form.sourceFunding">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.FinancialBack.InTotalRevenue')">
+            <span v-if="form.grossAnnualIncome == 1">{{
+              $t('Apply_info.FinancialBack.item61')
+            }}</span>
+            <span v-if="form.grossAnnualIncome == 2">{{
+              $t('Apply_info.FinancialBack.item62')
+            }}</span>
+            <span v-if="form.grossAnnualIncome == 3">{{
+              $t('Apply_info.FinancialBack.item63')
+            }}</span>
+            <span v-if="form.grossAnnualIncome == 4">{{
+              $t('Apply_info.FinancialBack.item64')
+            }}</span>
+            <span v-if="form.grossAnnualIncome == 5">{{
+              $t('Apply_info.FinancialBack.item65')
+            }}</span>
+            <span v-if="!form.grossAnnualIncome">{{ '--' }}</span>
+          </el-form-item>
+          <el-form-item :label="$t('Apply_info.FinancialBack.TotalAssets')">
+            <span v-if="form.totalNewWorth == 1">{{ $t('Apply_info.FinancialBack.item51') }}</span>
+            <span v-if="form.totalNewWorth == 2">{{ $t('Apply_info.FinancialBack.item52') }}</span>
+            <span v-if="form.totalNewWorth == 3">{{ $t('Apply_info.FinancialBack.item53') }}</span>
+            <span v-if="form.totalNewWorth == 4">{{ $t('Apply_info.FinancialBack.item54') }}</span>
+            <span v-if="form.totalNewWorth == 5">{{ $t('Apply_info.FinancialBack.item55') }}</span>
+            <span v-if="!form.totalNewWorth">{{ '--' }}</span>
+          </el-form-item>
+        </el-collapse-item>
+        <el-collapse-item :title="$t('Apply_info.ExperienceTrading.Title')" name="5">
+          <div style="text-align: left; margin: 10px 0">
+            <span>{{ $t('Apply_info.ExperienceTrading.Item1') }}</span>
+            <div>
+              <el-radio disabled v-model="form.experienceQualification" :label="1">
+                <span v-if="form.experienceQualification">{{ $t('Btn.Yes') }}</span>
+                <span v-if="!form.experienceQualification">{{ $t('Btn.No') }}</span>
+              </el-radio>
+            </div>
+          </div>
+          <div style="text-align: left; margin: 10px 0">
+            <span>{{ $t('Apply_info.ExperienceTrading.Item2') }}</span>
+            <div>
+              <el-radio disabled v-model="form.experienceTradingDerivative" :label="1">
+                <span v-if="form.experienceTradingDerivative">{{ $t('Btn.Yes') }}</span>
+                <span v-if="!form.experienceTradingDerivative">{{ $t('Btn.No') }}</span>
+              </el-radio>
+            </div>
+          </div>
+          <div style="text-align: left; margin: 10px 0">
+            <span>{{ $t('Apply_info.ExperienceTrading.Item3') }}</span>
+            <div>
+              <el-radio disabled v-model="form.experienceTradingForex" :label="1">
+                <span v-if="form.experienceTradingForex">{{ $t('Btn.Yes') }}</span>
+                <span v-if="!form.experienceTradingForex">{{ $t('Btn.No') }}</span>
+              </el-radio>
+            </div>
+          </div>
+          <div style="text-align: left; margin: 10px 0">
+            <span>{{ $t('Apply_info.ExperienceTrading.Item4') }}</span>
+            <div>
+              <el-radio disabled v-model="form.derivativeProducts" :label="1">
+                <span v-if="form.derivativeProducts">{{ $t('Btn.Yes') }}</span>
+                <span v-if="!form.derivativeProducts">{{ $t('Btn.No') }}</span>
+              </el-radio>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item
+          :title="$t('Apply_info.FileManagement.Title')"
+          name="6"
+          v-if="user.departmentId != 1"
+        >
+          <div class="Identity">
+            <span class="title">{{ $t('Apply_info.FileManagement.ProofIdentity') }}</span>
+            <span>1</span>
+            <span class="line"></span>
+            <el-button style="border: none" type="text">
+              <span v-if="files1.status == 1" @click="imgApprove(1)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="files1.status == 2" @click="imgApproveDe(1)">{{ $t('State.Pass') }}</span>
+              <span v-if="files1.status == 3" @click="imgApproveDe(1)">{{
+                $t('State.Refused')
+              }}</span>
+              <span v-if="files1.status == 4" @click="imgApprove(1)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="!files1.status">{{ $t('Placeholder.Not') }}</span>
+            </el-button>
+          </div>
+          <div class="Identity-img">
+            <div class="id-img">
+              <div
+                v-if="url1.substr(-3, 3) == 'pdf' || url1.substr(-3, 3) == 'PDF'"
+                class="pdfLink"
+              >
+                <a
+                  style="display: inline-block; width: 100%; height: 100%"
+                  :href="url1"
+                  target="_blank"
+                  ><i class="el-icon-document"></i
+                ></a>
+              </div>
+              <el-image
+                v-else
+                style="width: 100%; height: 100%"
+                :src="url1"
+                :preview-src-list="[url1]"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <i class="el-icon-picture-outline"></i>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+          </div>
+
+          <div class="Identity">
+            <span class="title">{{ $t('Apply_info.FileManagement.ProofIdentity') }}</span>
+            <span>2</span>
+            <span class="line"></span>
+            <el-button style="border: none" type="text">
+              <span v-if="files2.status == 1" @click="imgApprove(2)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="files2.status == 2" @click="imgApproveDe(2)">{{ $t('State.Pass') }}</span>
+              <span v-if="files2.status == 3" @click="imgApproveDe(2)">{{
+                $t('State.Refused')
+              }}</span>
+              <span v-if="files2.status == 4" @click="imgApprove(2)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="!files2.status">{{ $t('Placeholder.Not') }}</span>
+            </el-button>
+          </div>
+          <div class="Identity-img">
+            <div class="id-img">
+              <div
+                class="pdfLink"
+                v-if="url2.substr(-3, 3) == 'pdf' || url2.substr(-3, 3) == 'PDF'"
+              >
+                <a
+                  style="display: inline-block; width: 100%; height: 100%"
+                  :href="url2"
+                  target="_blank"
+                  ><i class="el-icon-document"></i
+                ></a>
+              </div>
+              <el-image
+                v-else
+                style="width: 100%; height: 100%"
+                :src="url2"
+                :preview-src-list="[url2]"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <i class="el-icon-picture-outline"></i>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+          </div>
+
+          <div class="Identity">
+            <span class="title">{{ $t('Apply_info.FileManagement.ProofAddress') }}</span>
+            <span class="line"></span>
+            <el-button style="border: none" type="text">
+              <span v-if="files3.status == 1" @click="imgApprove(3)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="files3.status == 2" @click="imgApproveDe(3)">{{ $t('State.Pass') }}</span>
+              <span v-if="files3.status == 3" @click="imgApproveDe(3)">{{
+                $t('State.Refused')
+              }}</span>
+              <span v-if="files3.status == 4" @click="imgApprove(3)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="!files3.status">{{ $t('Placeholder.Not') }}</span>
+            </el-button>
+          </div>
+          <div class="Identity-img">
+            <div class="id-img">
+              <div
+                class="pdfLink"
+                v-if="url3.substr(-3, 3) == 'pdf' || url3.substr(-3, 3) == 'PDF'"
+              >
+                <a
+                  style="display: inline-block; width: 100%; height: 100%"
+                  :href="url3"
+                  target="_blank"
+                  ><i class="el-icon-document"></i
+                ></a>
+              </div>
+              <el-image
+                v-else
+                style="width: 100%; height: 100%"
+                :src="url3"
+                :preview-src-list="[url3]"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <i class="el-icon-picture-outline"></i>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+          </div>
+          <div class="Identity">
+            <span class="title">{{ $t('Apply_info.FileManagement.AdditionalProof') }}</span>
+            <span class="line"></span>
+          </div>
+          <div class="Identity-img" v-for="(item, index) in filesOther" :key="index">
+            <div class="id-img">
+              <div
+                class="pdfLink"
+                v-if="item.urlOther.substr(-3, 3) == 'pdf' || item.urlOther.substr(-3, 3) == 'PDF'"
+              >
+                <a
+                  style="display: inline-block; width: 100%; height: 100%"
+                  :href="item.urlOther"
+                  target="_blank"
+                  ><i class="el-icon-document"></i
+                ></a>
+              </div>
+              <el-image
+                v-else
+                style="width: 100%; height: 100%"
+                :src="item.urlOther"
+                :preview-src-list="[item.urlOther]"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <i class="el-icon-picture-outline"></i>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+            <span class="line"></span>
+            <el-button style="border: none" type="text">
+              <span v-if="item.status == 1" @click="imgApprove(5, item.id)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="item.status == 2" @click="imgApproveDe(5, item.id)">{{
+                $t('State.Pass')
+              }}</span>
+              <span v-if="item.status == 3" @click="imgApproveDe(5, item.id)">{{
+                $t('State.Refused')
+              }}</span>
+              <span v-if="item.status == 4" @click="imgApprove(5, item.id)">{{
+                $t('State.ToBeProcessed')
+              }}</span>
+              <span v-if="!item.status">{{ $t('Placeholder.Not') }}</span>
+            </el-button>
+          </div>
+        </el-collapse-item>
+      </el-collapse>
+    </el-form>
+
+    <!-- 审批弹出 -->
+    <el-dialog
+      :title="$t('Customer_info.Files.Title')"
+      v-model:visible="dialogCheck"
+      center
+      append-to-body
+      @close="cancel"
+      custom-class="dialog_header_w"
+    >
+      <div class="dia-content">
+        <el-form
+          :model="dialogCheck_form"
+          :rules="rules"
+          ref="dialogCheckFormRef"
+          label-width="135px"
+          class="dialogCheck_form"
+        >
+          <el-form-item prop="" :label="$t('Label.Type') + ':'">
+            <span v-if="dialogCheck_form.type == 1 || dialogCheck_form.type == 2">{{
+              $t('Apply_info.FileManagement.ProofIdentity')
+            }}</span>
+            <span v-if="dialogCheck_form.type == 1">1</span>
+            <span v-if="dialogCheck_form.type == 2">2</span>
+            <span v-if="dialogCheck_form.type == 3">{{
+              $t('Apply_info.FileManagement.ProofAddress')
+            }}</span>
+            <span v-if="dialogCheck_form.type == 5">{{
+              $t('Apply_info.FileManagement.AdditionalProof')
+            }}</span>
+          </el-form-item>
+          <el-form-item prop="status" :label="$t('Label.CheckResults') + ':'">
+            <el-select
+              style="width: 400px"
+              class="crm_search_down"
+              size="small"
+              v-model="dialogCheck_form.status"
+              :placeholder="$t('Placeholder.Choose')"
+              @change="selectChange"
+            >
+              <el-option :label="$t('Apply_info.VerifiedUser.Refused')" :value="3"></el-option>
+              <el-option :label="$t('Apply_info.VerifiedUser.Agree')" :value="2"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            prop="approveDesc"
+            :label="$t('Label.Descr') + ':'"
+            v-if="dialogCheck_form.status == 3"
+          >
+            <el-select
+              style="width: 400px"
+              class="crm_search_down"
+              size="small"
+              v-model="dialogCheck_form.approveDesc"
+              :placeholder="$t('Placeholder.Choose')"
+              @change="selectChange"
+              filterable
+              allow-create
+            >
+              <el-option
+                v-for="item in reasons"
+                :key="item.id"
+                :label="Session.Get('lang') == 'cn' ? item.content : item.enContent"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="toVerified()">{{ $t('Btn.Confirm') }}</el-button>
+          <el-button @click="cancel">{{ $t('Btn.Cancel') }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 反审批弹出 -->
+    <el-dialog
+      :title="$t('Customer_info.Files.Title1')"
+      v-model:visible="dialogCheck1"
+      center
+      append-to-body
+      @close="cancel"
+      custom-class="dialog_header_w"
+    >
+      <div class="dia-content">
+        <el-form
+          :model="dialogCheck_form"
+          :rules="rules"
+          ref="dialogCheckFormRef"
+          label-width="135px"
+          class="dialogCheck_form"
+        >
+          <el-form-item prop="" :label="$t('Label.Type') + ':'">
+            <span v-if="dialogCheck_form.type == 1 || dialogCheck_form.type == 2">{{
+              $t('Apply_info.FileManagement.ProofIdentity')
+            }}</span>
+            <span v-if="dialogCheck_form.type == 1">1</span>
+            <span v-if="dialogCheck_form.type == 2">2</span>
+            <span v-if="dialogCheck_form.type == 3">{{
+              $t('Apply_info.FileManagement.ProofAddress')
+            }}</span>
+            <span v-if="dialogCheck_form.type == 5">{{
+              $t('Apply_info.FileManagement.AdditionalProof')
+            }}</span>
+          </el-form-item>
+          <el-form-item prop="status" :label="$t('Label.Action') + ':'">
+            <el-select
+              style="width: 400px"
+              class="crm_search_down"
+              size="small"
+              v-model="dialogCheck_form.status"
+              :placeholder="$t('Placeholder.Choose')"
+              @change="selectChange"
+            >
+              <el-option :label="$t('Customer_info.Real.Umpire')" :value="4"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="approveDesc" :label="$t('Label.Descr') + ':'">
+            <el-select
+              style="width: 400px"
+              class="crm_search_down"
+              size="small"
+              v-model="dialogCheck_form.approveDesc"
+              :placeholder="$t('Placeholder.Choose')"
+              @change="selectChange"
+              filterable
+              allow-create
+            >
+              <el-option
+                v-for="item in reasons"
+                :key="item.id"
+                :label="Session.Get('lang') == 'cn' ? item.content : item.enContent"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="toVerifiedDe()">{{ $t('Btn.Confirm') }}</el-button>
+          <el-button @click="cancel">{{ $t('Btn.Cancel') }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <ConsumerShareLink ref="consumerShareLink" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, computed, watch, nextTick, inject } from 'vue'
+  import Service from '@/service/customer'
+  import Config from '@/config'
+  import ConsumerShareLink from '@/views/global/ConsumerShareLink.vue'
+  import { useI18n } from 'vue-i18n'
+
+  const Session = inject('session')
+  const Pigeon = inject('pigeon')
+
+  const { t } = useI18n()
+
+  const { Code, Host85, Host00 } = Config
+
+  // Props
+  const props = defineProps({
+    dialogInfoCid: {
+      type: Boolean,
+      default: false,
+    },
+    type: {
+      default: 0,
+    },
+    btn: {
+      default: 0,
+    },
+    isTrading: {
+      default: false,
+    },
+    formInfo: {
+      default: '',
+    },
+  })
+
+  // Emits
+  const emit = defineEmits(['close'])
+
+  // 响应式数据
+  const reasons = ref({})
+  const imgUrl = ref(Host85)
+  const Host00Ref = ref(Host00)
+  const Host85Ref = ref(Host85)
+  const activeNames = ref(['1', '2', '3', '4', '5', '6', '7', '10'])
+  const form = ref({})
+  const info = ref({
+    CID: '123456',
+    name: 'QYY',
+  })
+  const dialogImageUrl = ref('')
+  const imageUrl = ref('')
+  const dialogVisible = ref(false)
+  const tradingAccount = ref([
+    { id: 1, account: '123456', type: '极速账户' },
+    { id: 2, account: '123456', type: '高级账户' },
+  ])
+  const cardIndex = ref(-1)
+
+  // 图片相关
+  const files1 = ref({})
+  const files2 = ref({})
+  const files3 = ref({})
+  const files5 = ref({})
+  const filesOther = ref([])
+  const url1 = ref('')
+  const url2 = ref('')
+  const url3 = ref('')
+  const url5 = ref('')
+  const srcList5 = ref([])
+  const srcList1 = ref([])
+  const srcList2 = ref([])
+  const srcList3 = ref([])
+
+  // 审批相关
+  const dialogCheck = ref(false)
+  const dialogCheck1 = ref(false)
+  const dialogCheck_form = ref({})
+
+  // 表单引用
+  const formRef = ref()
+  const dialogCheckFormRef = ref()
+  const consumerShareLinkRef = ref()
+
+  // 验证规则
+  const rules = reactive({
+    status: [
+      {
+        required: true,
+        message: '请选择',
+        trigger: 'blur',
+      },
+    ],
+    approveDesc: [
+      {
+        required: true,
+        message: '请选择',
+        trigger: 'blur',
+      },
+    ],
+  })
+
+  // 计算属性
+  const user = computed(() => {
+    try {
+      return JSON.parse(Session.Get('user', true) || '{}')
+    } catch (error) {
+      console.error('Error parsing user data:', error)
+      return {}
+    }
+  })
+
+  // 方法
+  const selectChange = () => {
+    // Vue 3 中通常不需要 $forceUpdate
+  }
+
+  const handleAvatarSuccess = (res, file) => {
+    imageUrl.value = URL.createObjectURL(file.raw)
+  }
+
+  // card
+  const cardTab = (index) => {
+    if (cardIndex.value == index) {
+      cardIndex.value = -1
+    } else {
+      cardIndex.value = index
+    }
+  }
+
+  // 关闭
+  const close = () => {
+    emit('close', false)
+  }
+
+  // 审批图片
+  const imgApprove = (val, id) => {
+    if (val == 1) {
+      dialogCheck_form.value.id = files1.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 2) {
+      dialogCheck_form.value.id = files2.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 3) {
+      dialogCheck_form.value.id = files3.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 5) {
+      dialogCheck_form.value.id = id
+      dialogCheck_form.value.customId = form.value.id
+    }
+    dialogCheck_form.value.type = val
+    dialogCheck.value = true
+  }
+
+  // 审核
+  const toVerified = async () => {
+    if (!dialogCheckFormRef.value) return
+
+    try {
+      const valid = await dialogCheckFormRef.value.validate()
+      if (valid) {
+        const res = await Service.customFileApprove({
+          ...dialogCheck_form.value,
+        })
+        if (res.code == Code.StatusOK) {
+          Pigeon.MessageOK(t('Msg.ModifySuccess'))
+          form.value.files?.forEach((item) => {
+            if (item.id == dialogCheck_form.value.id) {
+              if (item.type == 1) {
+                files1.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 2) {
+                files2.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 3) {
+                files3.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 10) {
+                filesOther.value.forEach((fileItem) => {
+                  if (fileItem.id == dialogCheck_form.value.id) {
+                    fileItem.status = dialogCheck_form.value.status
+                  }
+                })
+              }
+            }
+          })
+          cancel()
+        } else {
+          Pigeon.MessageError(res.msg)
+        }
+      }
+    } catch (error) {
+      // 验证失败
+    }
+  }
+
+  // 取消审核
+  const cancel = () => {
+    dialogCheck.value = false
+    dialogCheck1.value = false
+    if (dialogCheckFormRef.value) {
+      dialogCheckFormRef.value.resetFields()
+    }
+  }
+
+  // 反审批图片
+  const imgApproveDe = (val, id) => {
+    if (val == 1) {
+      dialogCheck_form.value.id = files1.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 2) {
+      dialogCheck_form.value.id = files2.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 3) {
+      dialogCheck_form.value.id = files3.value.id
+      dialogCheck_form.value.customId = form.value.id
+    } else if (val == 5) {
+      dialogCheck_form.value.id = id
+      dialogCheck_form.value.customId = form.value.id
+    }
+    dialogCheck_form.value.type = val
+    dialogCheck1.value = true
+  }
+
+  // 反审核
+  const toVerifiedDe = async () => {
+    if (!dialogCheckFormRef.value) return
+
+    try {
+      const valid = await dialogCheckFormRef.value.validate()
+      if (valid) {
+        const res = await Service.customFileApproveDe({
+          ...dialogCheck_form.value,
+        })
+        if (res.code == Code.StatusOK) {
+          Pigeon.MessageOK(t('Msg.ModifySuccess'))
+          form.value.files?.forEach((item) => {
+            if (item.id == dialogCheck_form.value.id) {
+              if (item.type == 1) {
+                files1.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 2) {
+                files2.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 3) {
+                files3.value.status = dialogCheck_form.value.status
+              }
+              if (item.type == 10) {
+                filesOther.value.forEach((fileItem) => {
+                  if (fileItem.id == dialogCheck_form.value.id) {
+                    fileItem.status = dialogCheck_form.value.status
+                  }
+                })
+              }
+            }
+          })
+          cancel()
+        } else {
+          Pigeon.MessageError(res.msg)
+        }
+      }
+    } catch (error) {
+      // 验证失败
+    }
+  }
+
+  // 获取原因列表
+  const searchReasons = async () => {
+    const res = await Service.reasonsRefusalList({ type: 1 })
+    if (res.code == Code.StatusOK) {
+      if (res.data == null) {
+        reasons.value = {}
+      } else {
+        reasons.value = res.data
+      }
+    } else {
+      Pigeon.MessageError(res.msg)
+    }
+  }
+
+  const CopyShareLink = () => {
+    nextTick(() => {
+      if (props.formInfo.isAgent) {
+        consumerShareLinkRef.value?.LinkActivity1({
+          form: form.value,
+          type: 2,
+        })
+      }
+    })
+  }
+
+  // 监听器
+  watch(
+    () => props.dialogInfoCid,
+    (type) => {
+      if (type == false) {
+        if (formRef.value) {
+          formRef.value.resetFields()
+        }
+        form.value = {}
+      }
+    }
+  )
+
+  watch(
+    () => props.formInfo,
+    () => {
+      searchReasons()
+      form.value = props.formInfo
+      url1.value = ''
+      files1.value = {}
+      url2.value = ''
+      files2.value = {}
+      url3.value = ''
+      files3.value = {}
+      url5.value = ''
+      files5.value = {}
+      filesOther.value = []
+
+      if (form.value.files?.length) {
+        filesOther.value = []
+        form.value.files.forEach((item) => {
+          if (item.type == 1) {
+            files1.value = item
+            url1.value = Host85Ref.value + (item.againPath || item.path)
+            srcList1.value = [url1.value]
+          }
+          if (item.type == 2) {
+            files2.value = item
+            url2.value = Host85Ref.value + (item.againPath || item.path)
+            srcList2.value = [url2.value]
+          }
+          if (item.type == 3) {
+            files3.value = item
+            url3.value = Host85Ref.value + (item.againPath || item.path)
+            srcList3.value = [url3.value]
+          }
+          if (item.type == 10) {
+            item.urlOther = Host85Ref.value + (item.againPath || item.path)
+            filesOther.value.push(item)
+          }
+        })
+      } else {
+        url1.value = ''
+        files1.value = {}
+        url2.value = ''
+        files2.value = {}
+        url3.value = ''
+        files3.value = {}
+        url5.value = ''
+        files5.value = {}
+        filesOther.value = []
+      }
+    }
+  )
+</script>
+
+<style lang="scss" scoped>
+  @import 'index.scss';
+</style>

+ 1 - 154
src/views/home/index.vue

@@ -1,159 +1,6 @@
 <template>
   <div class="home-container">
-    <el-row class="row-bg" :gutter="10">
-      <el-col :xs="24" :sm="12" :lg="8">
-        <el-card class="box-card" style="height: 100%">
-          <!--          <el-image-->
-          <!--              class="wechat" -->
-          <!--              :src="weLogo"-->
-          <!--              :preview-src-list="[weLogo]"-->
-          <!--              style="max-width: 200px"-->
-          <!--              :data-resid="Date.now()"-->
-          <!--          />-->
-          <div class="personal">
-            <div>
-              <el-avatar :size="50" :src="AvatarLogo" />
-            </div>
-            <div class="name"></div>
-            <div class="description"></div>
-            <div class="list">
-              <div>昵称:小狼</div>
-              <div>职业:前端</div>
-              <div>公司:小公司</div>
-              <div>年龄:~~</div>
-              <div>性别:男</div>
-              <div>现住址:中国-浙江-杭州</div>
-              <div>邮箱:1135957121@qq.com</div>
-              <div>微信:19550102670(欢迎加微信入群,群员已超200)</div>
-              <div>技术栈:JavaScript、HTML、CSS、Vue、Node、React</div>
-              <div>~~~接外包~~~</div>
-            </div>
-            <el-divider></el-divider>
-            <div style="margin-bottom: 15px"><h5>个性标签</h5></div>
-            <div>
-              <el-tag style="margin-right: 10px">怕麻烦</el-tag>
-              <el-tag style="margin-right: 10px">健身运动</el-tag>
-              <el-tag style="margin-right: 10px">睡觉</el-tag>
-              <el-tag style="margin-right: 10px">漫威</el-tag>
-              <el-tag>向往</el-tag>
-            </div>
-            <el-divider></el-divider>
-            <div style="margin-bottom: 15px"><h5>最喜欢的一句话</h5></div>
-            <div>---------- 开心最重要</div>
-            <el-divider></el-divider>
-            <div style="margin-bottom: 15px">
-              <h5>
-                如果对你有帮助的话,可以麻烦点一颗 Star、Fork、Watch! 你的鼓励是我继续优化的动力~~
-              </h5>
-            </div>
-          </div>
-        </el-card>
-      </el-col>
-      <el-col :xs="24" :sm="12" :lg="16">
-        <el-row class="custom" :gutter="10">
-          <el-col :xs="24" :sm="12" :lg="8">
-            <el-card style="margin-bottom: 10px">
-              <div class="grid-content">
-                <div class="left">
-                  <el-icon style="font-size: 24px; color: white"><user /></el-icon>
-                </div>
-                <div class="right">
-                  <div class="h2" style="color: #2d8cf0">
-                    <count-to
-                      :start-val="0"
-                      :end-val="5268"
-                      :duration="2000"
-                      :autoplay="true"
-                    ></count-to>
-                  </div>
-                  <div>用户访问量</div>
-                </div>
-              </div>
-            </el-card>
-          </el-col>
-          <el-col :xs="24" :sm="12" :lg="8">
-            <el-card style="margin-bottom: 10px">
-              <div class="grid-content">
-                <div class="left" style="background: #64d572">
-                  <el-icon style="font-size: 24px; color: white"><user /></el-icon>
-                </div>
-                <div class="right">
-                  <div class="h2" style="color: #64d572">
-                    <count-to
-                      :start-val="0"
-                      :end-val="9599"
-                      :duration="2000"
-                      :autoplay="true"
-                    ></count-to>
-                  </div>
-                  <div>系统消息</div>
-                </div>
-              </div>
-            </el-card>
-          </el-col>
-          <el-col :xs="24" :sm="12" :lg="8">
-            <el-card style="margin-bottom: 10px">
-              <div class="grid-content">
-                <div class="left" style="background: #f25e43">
-                  <el-icon style="font-size: 24px; color: white"><user /></el-icon>
-                </div>
-                <div class="right">
-                  <div class="h2" style="color: #f25e43">
-                    <count-to
-                      :start-val="0"
-                      :end-val="595453"
-                      :duration="2000"
-                      :autoplay="true"
-                    ></count-to>
-                  </div>
-                  <div>数量</div>
-                </div>
-              </div>
-            </el-card>
-          </el-col>
-        </el-row>
-        <el-card class="box-card">
-          <template #header>
-            <div class="card-header">
-              <span>系列开源产品</span>
-            </div>
-          </template>
-          <div style="display: flex">
-            <el-card
-              style="flex: 1; margin-right: 20px; cursor: pointer"
-              class="card-item"
-              @click="goTo('https://ext.dcloud.net.cn/plugin?id=7511')"
-            >
-              <div style="color: white; margin-bottom: 10px"><h3>zb-table</h3></div>
-              <div style="font-size: 12px; color: white">
-                uniapp 表格组件
-                支持固定表头和首列、上拉加载更多、及固定多列,表格自适应内容,排序,多选checkbox、可点击删除,编辑、合计功能,兼容多端
-              </div>
-            </el-card>
-            <el-card
-              style="flex: 1; cursor: pointer"
-              class="card-item"
-              @click="goTo('https://github.com/zouzhibin/vue-admin-perfect')"
-            >
-              <div style="color: white; margin-bottom: 10px"><h3>vue-admin-perfect</h3></div>
-              <div style="font-size: 12px; color: white">
-                系统基于vue3+vuex+ element-plus+ts后台管理系统
-              </div>
-            </el-card>
-          </div>
-        </el-card>
-        <el-card class="box-card">
-          <template #header>
-            <div class="card-header">
-              <span>每周用户活跃量</span>
-            </div>
-          </template>
-          <div>
-            <bar-charts id="bar1" height="300px" width="100%"></bar-charts>
-          </div>
-        </el-card>
-      </el-col>
-    </el-row>
+    <el-row class="row-bg" :gutter="10"> </el-row>
   </div>
 </template>
 <script setup lang="ts">

+ 749 - 0
src/views/home2/Home.vue

@@ -0,0 +1,749 @@
+<template>
+  <el-container id="home" v-loading="loading">
+    <el-aside width="auto">
+      <nav-menu></nav-menu>
+    </el-aside>
+    <el-container>
+      <el-header>
+        <el-row :gutter="10">
+          <el-col :span="2">
+            <div class="btn-control" @click="ChangeCollapse">
+              <i class="el-icon-s-fold"></i>
+            </div>
+          </el-col>
+          <el-col :span="22" class="tool-box">
+            <el-button
+              v-if="isQAlist"
+              plain
+              round
+              size="mini"
+              type="primary"
+              icon="el-icon-chat-line-round"
+              style="margin-right: 20px"
+              @click="openQA"
+              >{{ 'Q&A' }}</el-button
+            >
+            <el-dropdown trigger="click" class="language">
+              <span class="el-dropdown-link">
+                <span>{{ langName }}</span
+                ><i class="el-icon-arrow-down el-icon--right"></i>
+              </span>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item @click="SwitchLanguage('cn')">中文简体</el-dropdown-item>
+                  <el-dropdown-item @click="SwitchLanguage('en')">English</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+            <el-dropdown v-if="info.roteCode == 'ROLE_SALE'" trigger="click" class="role-switch">
+              <span class="el-dropdown-link">
+                <span>{{ currentRoleName }}</span
+                ><i class="el-icon-arrow-down el-icon--right"></i>
+              </span>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item
+                    v-for="item in attachList"
+                    :key="item.id"
+                    :class="{ 'is-active': item.id === currentAttachId }"
+                    @click="switchRole(item)"
+                  >
+                    {{ item.name }}
+                    <i v-if="item.id === currentAttachId" class="el-icon-check"></i>
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+            <i class="crm-cursor el-icon-bell icon" style="font-size: 20px"></i>
+            <i class="crm-cursor iconquanping iconfont icon" @click="fullScreen"></i>
+            <el-dropdown trigger="click" class="username crm-cursor">
+              <span class="el-dropdown-link">
+                <span>{{ user.name || 'UserName' }}</span
+                ><i class="el-icon-arrow-down el-icon--right"></i>
+              </span>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item @click="updatePwd"
+                    ><span>
+                      {{ $t('Label.UpdatePwd') }}
+                    </span></el-dropdown-item
+                  >
+                  <el-dropdown-item @click="beforeLogout">Logout</el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </el-col>
+        </el-row>
+      </el-header>
+      <el-main>
+        <el-row class="tool">
+          <el-col :span="16">
+            <bread-crumb></bread-crumb>
+          </el-col>
+          <el-col :span="8">
+            <span>
+              {{ $t('Home1.Time') }}
+            </span>
+            <span class="crm-cursor" @click="toReload">{{ time }}</span>
+            <i class="crm-cursor el-icon-refresh-right" @click="toReload"></i>
+          </el-col>
+        </el-row>
+        <tab-menu></tab-menu>
+        <div class="container">
+          <router-view />
+        </div>
+      </el-main>
+    </el-container>
+
+    <!-- 修改密码弹出 -->
+    <el-dialog
+      v-model:visible="dialogCheck"
+      :title="$t('Label.UpdatePwd')"
+      center
+      custom-class="dialog_header_w"
+    >
+      <div class="dia-content">
+        <el-form
+          ref="dialogCheckFormRef"
+          :model="dialogCheck_form"
+          :rules="rules"
+          label-width="135px"
+          class="dialogCheck_form"
+        >
+          <el-form-item prop="oldPassword" :label="$t('Label.oldPwd') + ':'">
+            <el-input
+              v-model.trim="dialogCheck_form.oldPassword"
+              size="small"
+              :placeholder="$t('Placeholder.Input')"
+            ></el-input>
+          </el-form-item>
+          <el-form-item prop="newPassword" :label="$t('Label.newPwd') + ':'">
+            <el-input
+              v-model.trim="dialogCheck_form.newPassword"
+              size="small"
+              :placeholder="$t('Placeholder.Input')"
+            ></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="toVerified()">
+            {{ $t('Btn.Confirm') }}
+          </el-button>
+          <el-button @click="cancel">
+            {{ $t('Btn.Cancel') }}
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- qa弹出 -->
+    <el-dialog
+      v-model:visible="dialogCheckQA"
+      :title="'Q&A'"
+      center
+      width="80%"
+      custom-class="dialog_header_w dialog_header_qa"
+    >
+      <div class="dia-content">
+        <qa-list
+          v-if="qaType == 1 && dialogCheckQA"
+          @to-new="toNew"
+          @to-update="toUpdate"
+          @to-check="toCheck"
+        >
+        </qa-list>
+        <qa-add
+          v-if="qaType == 2 && dialogCheckQA"
+          :my-id-q-a="myId"
+          :type-q-a="type"
+          @cancel-qa="cancelQa"
+        >
+        </qa-add>
+      </div>
+    </el-dialog>
+  </el-container>
+</template>
+
+<script lang="ts" setup>
+  import {
+    ref,
+    reactive,
+    computed,
+    watch,
+    onMounted,
+    onUnmounted,
+    nextTick,
+    inject,
+    onBeforeMount,
+  } from 'vue'
+  import { useRouter, useRoute } from 'vue-router'
+  import { useStore } from 'vuex'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { FullScreen, Refresh, ArrowDown } from '@element-plus/icons-vue'
+  import axios from 'axios'
+  import { useI18n } from 'vue-i18n'
+
+  // 组件导入
+  import NavMenu from '@/components/NavMenu'
+  import BreadCrumb from '@/components/BreadCrumb'
+  import TabMenu from '@/components/TabMenu'
+  // import QaList from './global/QAList'
+  // import QaAdd from './global/QAAdd'
+  import Service from '@/service/login'
+  import Config from '@/config/index'
+  import { safeGetUser, safeGetDisplay } from '@/utils/safeJson'
+
+  const { Code } = Config
+  const { t, locale } = useI18n()
+  // 引入注入的全局
+  const session = inject('session')
+  const reload = inject('reload')
+
+  // Vue 相关
+  const router = useRouter()
+  const route = useRoute()
+  const store = useStore()
+
+  // 响应式数据
+  const info = ref({})
+  const loading = ref(false)
+  const dialogCheck = ref(false)
+  const dialogCheck_form = reactive({
+    oldPassword: '',
+    newPassword: '',
+  })
+  const time = ref('')
+  const langName = ref('')
+  const isCollapse = ref(false)
+  const screenWidth = ref(
+    window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
+  )
+
+  const dialogCheckQA = ref(false)
+  const qaType = ref(1)
+  const myId = ref('')
+  const type = ref('')
+  const isQAlist = ref(false)
+  const attachList = ref([])
+  const currentAttachId = ref(null)
+
+  const dialogCheckFormRef = ref(null)
+
+  // 语言映射
+  const langs = {
+    cn: '中文简体',
+    en: 'English',
+  }
+
+  // 计算属性
+  const user = computed(() => safeGetUser(session))
+  const expire = computed(() => store.state.home.expire)
+  const currentRoleName = computed(() => {
+    if (attachList.value.length === 0) return ''
+    const currentRole = attachList.value.find((item) => item.id === currentAttachId.value)
+    return currentRole ? currentRole.name : ''
+  })
+
+  // 验证规则
+  const rules = reactive({
+    oldPassword: [
+      {
+        required: true,
+        message: t('vaildate.input.empty'),
+        trigger: 'blur',
+      },
+    ],
+    newPassword: [
+      {
+        required: true,
+        message: t('vaildate.input.empty'),
+        trigger: 'blur',
+      },
+    ],
+  })
+
+  // 初始化权限
+  const initDisplay = () => {
+    let display = {}
+    const userData = safeGetUser(session)
+    if (userData && userData.display) {
+      userData.display.forEach((item) => {
+        item.children.forEach((item1) => {
+          item1.children = Object.values(item1.btns)
+          item1.children.forEach((item2) => {
+            display[item2.code] = item2
+          })
+        })
+      })
+    }
+    session.Set('display', JSON.stringify(display))
+  }
+
+  // QA相关方法
+  const openQA = () => {
+    dialogCheckQA.value = true
+    qaType.value = 1
+  }
+
+  const toNew = (val) => {
+    const link = window.location.href
+    if (link.indexOf('system/qa/add') > -1) {
+      router.push({ path: '/system/qa/list' }).catch((arr) => arr)
+    }
+    dialogCheckQA.value = true
+    myId.value = -1
+    type.value = 'add'
+    qaType.value = val
+  }
+
+  const cancelQa = () => {
+    qaType.value = 1
+  }
+
+  const toUpdate = (id) => {
+    const link = window.location.href
+    if (link.indexOf('system/qa/add') > -1) {
+      router.push({ path: '/system/qa/list' }).catch((arr) => arr)
+    }
+    dialogCheckQA.value = true
+    myId.value = id
+    type.value = 'update'
+    qaType.value = 2
+  }
+
+  const toCheck = (id) => {
+    dialogCheckQA.value = true
+    myId.value = id
+    type.value = 'check'
+    qaType.value = 2
+  }
+
+  const isQAlistGet = () => {
+    const display = safeGetDisplay(sessionStorage)
+    if (display && display['R-System-QAList-Search'] && display['R-System-QAList-Search'].show) {
+      isQAlist.value = true
+    } else {
+      isQAlist.value = false
+    }
+  }
+
+  // 退出登录
+  const beforeLogout = () => {
+    ElMessageBox.confirm('确定要退出登录吗?', '系统提示', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }).then(() => {
+      logout()
+    })
+  }
+
+  const logout = async () => {
+    const res = await Service.Logout({})
+    if (res.code == Code.StatusOK) {
+      sessionStorage.clear()
+      router.push({ path: '/signin' })
+      store.commit('InfoExpire', false)
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  // 全屏功能
+  const fullScreen = () => {
+    if (!isFullScreen()) {
+      const docElm = document.documentElement
+      if (docElm.requestFullscreen) {
+        docElm.requestFullscreen()
+      } else if (docElm.mozRequestFullScreen) {
+        docElm.mozRequestFullScreen()
+      } else if (docElm.webkitRequestFullScreen) {
+        docElm.webkitRequestFullScreen()
+      } else if (docElm.msRequestFullscreen) {
+        docElm.msRequestFullscreen()
+      }
+    } else {
+      if (document.exitFullscreen) {
+        document.exitFullscreen()
+      } else if (document.mozCancelFullScreen) {
+        document.mozCancelFullScreen()
+      } else if (document.webkitCancelFullScreen) {
+        document.webkitCancelFullScreen()
+      } else if (document.msExitFullscreen) {
+        document.msExitFullscreen()
+      }
+    }
+  }
+
+  const isFullScreen = () => {
+    return !!(
+      document.fullscreen ||
+      document.mozFullScreen ||
+      document.webkitIsFullScreen ||
+      document.webkitFullScreen ||
+      document.msFullScreen
+    )
+  }
+
+  // 刷新
+  const toReload = () => {
+    reload()
+    getDate()
+  }
+
+  // 时间
+  const getDate = () => {
+    const timezone = 3
+    const offset_GMT = new Date().getTimezoneOffset()
+    const nowDate = new Date().getTime()
+    const now = new Date(nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000)
+
+    let month = now.getMonth() + 1
+    let day = now.getDate()
+    let hh = now.getHours()
+    let mm = now.getMinutes()
+
+    if (month < 10) month = '0' + month
+    if (day < 10) day = '0' + day
+    if (hh < 10) hh = '0' + hh
+    if (mm < 10) mm = '0' + mm
+
+    time.value = `${month}/${day} ${hh}:${mm}`
+  }
+
+  // 侧边栏控制
+  const ChangeCollapse = () => {
+    isCollapse.value = !isCollapse.value
+    store.commit('SetCollapse', isCollapse.value)
+  }
+
+  // 语言切换
+  const SwitchLanguage = (lang) => {
+    if (lang !== session.Get('lang')) {
+      switchLang(lang)
+    }
+  }
+
+  const switchLang = async (lang) => {
+    // 这里需要根据实际情况处理i18n
+    locale.value = lang
+    // 如果是使用 vue-i18n@next,可能需要通过 useI18n() 来处理
+    langName.value = langs[lang]
+    session.Set('lang', lang)
+    axios.defaults.headers.common['Language'] = lang
+    setTimeout(() => {
+      reload()
+    }, 800)
+  }
+
+  // 修改密码
+  const updatePwd = () => {
+    dialogCheck.value = true
+  }
+
+  const toVerified = async () => {
+    if (!dialogCheckFormRef.value) return
+
+    try {
+      const valid = await dialogCheckFormRef.value.validate()
+      if (valid) {
+        loading.value = true
+        const res = await Service.updatePwd({
+          oldPassword: dialogCheck_form.oldPassword,
+          newPassword: dialogCheck_form.newPassword,
+        })
+        if (res.code == Code.StatusOK) {
+          ElMessage.success('修改成功')
+          cancel()
+        } else {
+          ElMessage.error(res.msg)
+        }
+      }
+    } catch (error) {
+      // 验证失败
+    } finally {
+      loading.value = false
+    }
+  }
+
+  const cancel = () => {
+    dialogCheck.value = false
+    if (dialogCheckFormRef.value) {
+      dialogCheckFormRef.value.resetFields()
+    }
+  }
+
+  // 角色相关
+  const getAttachList = async () => {
+    try {
+      const res = await Service.getUserAttachs({})
+      if (res.code == Code.StatusOK) {
+        attachList.value = res.data || []
+        if (attachList.value.length > 0) {
+          const userData = user.value
+          if (userData && userData.id) {
+            currentAttachId.value = userData.id
+          } else if (!currentAttachId.value) {
+            currentAttachId.value = attachList.value[0].id
+          }
+        }
+      } else {
+        ElMessage.error(res.msg)
+      }
+    } catch (error) {
+      console.error('获取角色列表失败:', error)
+    }
+  }
+
+  const switchRole = (item) => {
+    if (item.id === currentAttachId.value) return
+
+    ElMessageBox.confirm('是否切换身份?', '系统提示', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }).then(async () => {
+      await confirmSwitchRole(item.id)
+    })
+  }
+
+  const confirmSwitchRole = async (attachId) => {
+    try {
+      loading.value = true
+      const res = await Service.switchLogin({ attachId })
+      if (res.code == Code.StatusOK) {
+        if (res.data) {
+          sessionStorage.setItem('access_token', res.data)
+          axios.defaults.headers.common['Access-Token'] = res.data
+        }
+        await nextTick()
+        await getLoginInfo(attachId)
+      } else {
+        ElMessage.error(res.msg)
+      }
+    } catch (error) {
+      console.error('切换角色失败:', error)
+      ElMessage.error('切换角色失败')
+    } finally {
+      loading.value = false
+    }
+  }
+
+  const getLoginInfo = async () => {
+    const res = await Service.CustomLoginInfo({})
+    if (res.code == Code.StatusOK) {
+      store.commit('InitInfo', res.data)
+      store.commit('InfoExpire', false)
+      sessionStorage.setItem('info', JSON.stringify(res.data))
+      ElMessage.success('登录成功')
+      setTimeout(() => {
+        loading.value = false
+        window.location.reload()
+        router.push({ path: '/' }).catch((arr) => arr)
+      }, 1000)
+    } else {
+      store.commit('InfoExpire', false)
+      ElMessage.error('系统错误')
+      loading.value = false
+    }
+  }
+
+  // 初始化语言
+  const InitLanguage = () => {
+    if (!session.Get('lang')) {
+      session.Set('lang', 'cn')
+    }
+    const lang = session.Get('lang') || 'cn'
+    // 这里需要根据实际情况处理i18n
+    locale.value = lang
+    langName.value = langs[lang]
+  }
+
+  // 响应式布局
+  const ListenResize = () => {
+    setTimeout(() => {
+      const width = screenWidth.value
+      if (width <= 1080) {
+        isCollapse.value = true
+        store.commit('SetCollapse', isCollapse.value)
+      } else {
+        isCollapse.value = sessionStorage.getItem('isCollapse') === 'true'
+      }
+    }, 20)
+  }
+
+  const resize = () => {
+    window.onresize = () => {
+      const width =
+        window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
+      screenWidth.value = width
+    }
+  }
+
+  // 生命周期
+  onBeforeMount(() => {
+    try {
+      const infoData = session.Get('info')
+      info.value = infoData ? JSON.parse(infoData) : {}
+    } catch (error) {
+      console.error('Error parsing info data:', error)
+      info.value = {}
+    }
+  })
+  onMounted(() => {
+    // 初始化权限
+    initDisplay()
+
+    // 初始化侧边栏状态
+    isCollapse.value = session.Get('isCollapse') === 'true'
+    InitLanguage()
+    ListenResize()
+    resize()
+    getDate()
+    isQAlistGet()
+    getAttachList()
+  })
+
+  onUnmounted(() => {
+    window.onresize = null
+  })
+
+  // 监听器
+  watch(screenWidth, (val) => {
+    screenWidth.value = val
+    ListenResize()
+  })
+
+  watch(route, () => {
+    getDate()
+  })
+
+  watch(expire, (val) => {
+    if (val) {
+      ElMessageBox.confirm('登录已过期,请重新登录', '系统提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(async () => {
+          const res = await Service.Logout({})
+          if (res.code == Code.StatusOK) {
+            sessionStorage.clear()
+            router.push({ path: '/signin' })
+            store.commit('InfoExpire', false)
+          } else {
+            ElMessage.error(res.msg)
+          }
+        })
+        .catch(() => {
+          store.commit('InfoExpire', false)
+        })
+    }
+  })
+</script>
+
+<style scoped lang="scss">
+  #home {
+    width: 100%;
+    height: 100%;
+
+    .el-header {
+      height: 60px;
+      background-color: #ffffff;
+      color: #333;
+      line-height: 60px;
+
+      .btn-control {
+        float: left;
+
+        i {
+          font-size: 24px;
+
+          &:hover {
+            cursor: pointer;
+          }
+        }
+      }
+      .tool-box {
+        text-align: right;
+        .username {
+          margin: 0 20px;
+        }
+        .icon {
+          font-size: 18px;
+          margin-left: 20px;
+        }
+        .language {
+          .el-dropdown-link {
+            background-color: rgba(247, 247, 250, 1);
+            border: none;
+            border-radius: 30px;
+            padding: 6px 20px;
+            cursor: pointer;
+          }
+        }
+        .role-switch {
+          margin-left: 20px;
+          .el-dropdown-link {
+            background-color: rgba(247, 247, 250, 1);
+            border: none;
+            border-radius: 30px;
+            padding: 6px 20px;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+
+    .el-aside {
+      background-color: rgba(43, 48, 67, 1);
+      color: #ffffff;
+      overflow: hidden;
+      overflow-y: auto;
+    }
+
+    .el-main {
+      padding: 15px;
+      overflow: hidden;
+      background-color: #f5f5f5;
+
+      .tool {
+        margin-bottom: 15px;
+        text-align: right;
+        font-size: 14px;
+        i.el-icon-refresh-right {
+          margin-left: 10px;
+          font-weight: bold;
+        }
+      }
+
+      .container {
+        height: calc(100% - 75px);
+        overflow: hidden;
+        position: relative;
+      }
+    }
+
+    body > .el-container {
+      margin-bottom: 40px;
+    }
+  }
+</style>
+<style lang="scss">
+  .dialog_header_qa.dialog_header_w.el-dialog {
+    min-height: 80%;
+  }
+
+  // 角色切换下拉框样式
+  .el-dropdown-menu .el-dropdown-item.is-active {
+    color: #409eff;
+    background-color: #f5f7fa;
+
+    .el-icon-check {
+      float: right;
+      color: #409eff;
+    }
+  }
+</style>

+ 0 - 111
src/views/login/components/LoginForm.vue

@@ -1,111 +0,0 @@
-<template>
-  <div class="login-title">
-    <img class="icon" src="@/assets/image/logo.png" alt="logo" />
-    <h2 class="title">Vue-Admin-Perfect</h2>
-  </div>
-  <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules">
-    <el-form-item label="" prop="username">
-      <el-input
-        v-model="ruleForm.username"
-        placeholder="请输入用户名"
-        auto-complete="on"
-        style="position: relative"
-        @keyup.enter="submitForm(ruleFormRef)"
-      >
-        <template #prefix>
-          <el-icon class="el-input__icon"><UserFilled /></el-icon>
-        </template>
-      </el-input>
-    </el-form-item>
-
-    <el-form-item label="" prop="password">
-      <el-input
-        v-model="ruleForm.password"
-        placeholder="请输入密码"
-        auto-complete="on"
-        :type="passwordType"
-        @keyup.enter="submitForm(ruleFormRef)"
-      >
-        <template #prefix>
-          <el-icon class="el-input__icon"><GoodsFilled /></el-icon>
-        </template>
-        <template #suffix>
-          <div class="show-pwd" @click="showPwd">
-            <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
-          </div>
-        </template>
-      </el-input>
-    </el-form-item>
-
-    <el-form-item style="width: 100%">
-      <el-button
-        :loading="loading"
-        class="login-btn"
-        type="primary"
-        @click="submitForm(ruleFormRef)"
-      >
-        登录
-      </el-button>
-    </el-form-item>
-  </el-form>
-</template>
-<script lang="ts" setup>
-  import { ref, reactive } from 'vue'
-  import type { FormInstance } from 'element-plus'
-  import { ElNotification } from 'element-plus'
-  import { useRouter } from 'vue-router'
-  import { useUserStore } from '@/store/modules/user'
-  import { getTimeStateStr } from '@/utils/index'
-
-  const router = useRouter()
-  const UserStore = useUserStore()
-  const ruleFormRef = ref<FormInstance>()
-  const passwordType = ref('password')
-  const loading = ref(false)
-
-  const rules = reactive({
-    password: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
-    username: [{ required: true, message: '请输入密码', trigger: 'blur' }],
-  })
-
-  // 表单数据
-  const ruleForm = reactive({
-    username: 'admin',
-    password: '123456',
-  })
-
-  // 显示密码图标
-  const showPwd = () => {
-    passwordType.value = passwordType.value === 'password' ? '' : 'password'
-  }
-
-  const submitForm = (formEl: FormInstance | undefined) => {
-    if (!formEl) return
-    formEl.validate((valid) => {
-      if (valid) {
-        loading.value = true
-        // 登录
-        setTimeout(async () => {
-          await UserStore.login(ruleForm)
-          await router.push({
-            path: '/',
-          })
-          ElNotification({
-            title: getTimeStateStr(),
-            message: '欢迎登录 Vue Admin Perfect',
-            type: 'success',
-            duration: 3000,
-          })
-          loading.value = true
-        }, 1000)
-      } else {
-        console.log('error submit!')
-        return false
-      }
-    })
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import '../index.scss';
-</style>

+ 0 - 14
src/views/login/components/LoginQrcode.vue

@@ -1,14 +0,0 @@
-<template>
-  <div class="login-qrcode">
-    <h2 class="title">Vue-Admin-Perfect</h2>
-    <vue-qr text="https://github.com/zouzhibin/vue-admin-perfect"></vue-qr>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import vueQr from 'vue-qr/src/packages/vue-qr.vue'
-</script>
-
-<style lang="scss" scoped>
-  @import '../index';
-</style>

+ 184 - 0
src/views/page/Page.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="pageCainer">
+    <keep-alive :include="cachedComponents">
+      <router-view></router-view>
+    </keep-alive>
+    <!-- 高风险客户内转提醒 -->
+    <div class="financeRisk" v-if="financeRisk.length">
+      <div class="financeRisk_box" v-for="item in financeRisk" :key="item.id">
+        <div style="margin-top: 5px">
+          <span>
+            {{ $t('Dashboard.item1') }}
+          </span>
+          <span>{{ ':' }}{{ item.name }}</span>
+          <span></span>
+          <span>{{ '(' }}{{ item.cId }}{{ ')' }}</span>
+        </div>
+        <div style="margin-top: 10px">
+          <span>{{ item.withdrawLogin }}</span>
+          <span>
+            {{ $t('Dashboard.item2') }}
+          </span>
+          <span>${{ item.amount }}</span>
+          <span>
+            {{ $t('Dashboard.item3') }}
+          </span>
+          <span>{{ item.depositLogin }}</span>
+        </div>
+        <div class="dialog-footer">
+          <el-button size="mini" type="primary" @click="toVerified(item.id, item)">
+            {{ $t('Dashboard.item4') }}
+          </el-button>
+          <el-button size="mini" @click="cancel(item)">
+            {{ $t('Dashboard.item5') }}
+          </el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, onMounted, onUnmounted, inject, computed } from 'vue'
+  import { useRouter } from 'vue-router'
+  import Service from '@/service/login'
+  import Config from '@/config/index'
+  // 这个数据放到旁边文件了
+  import { cachedComponents } from './cachedRoute'
+  // 引用注入
+  const Session = inject('session')
+  const Pigeon = inject('pigeon')
+
+  const { Code } = Config
+  const router = useRouter()
+
+  // 响应式数据
+  const interval = ref(null)
+  const financeRisk = ref([])
+  const closeId = ref([])
+
+  const display = computed(() => JSON.parse(Session.Get('display', true)))
+
+  // 计时器--高风险客户内转提醒
+  const beginTimer = () => {
+    interval.value = setInterval(() => {
+      closeId.value.forEach((item) => {
+        item.downTime = item.downTime - 1
+      })
+      closeId.value = closeId.value.filter((item) => {
+        return item.downTime > 0
+      })
+
+      getFinanceRiskList()
+    }, 60000)
+  }
+
+  const getFinanceRiskList = async () => {
+    try {
+      const res = await Service.financeTransferRiskRemindList({})
+      if (res.code == Code.StatusOK) {
+        if (res.data && res.data.length) {
+          let financeRiskData = financeRisk.value.concat(res.data)
+          let map = new Map()
+          for (let item of financeRiskData) {
+            map.set(item.id, item)
+          }
+          financeRiskData = [...map.values()]
+          financeRisk.value = financeRiskData.filter((item) => {
+            return !closeId.value.some((ele) => ele.id === item.id)
+          })
+        }
+      } else {
+        Pigeon.MessageError(res.msg)
+      }
+    } catch (error) {
+      console.error('获取财务风险列表失败:', error)
+    }
+  }
+
+  // 处理
+  const toVerified = async (id, data) => {
+    try {
+      const res = await Service.financeTransferRiskRemindConfirm({ id: id })
+      if (res.code == Code.StatusOK) {
+        Pigeon.MessageOK(res.msg)
+        cancel(data, 1)
+      } else {
+        Pigeon.MessageError(res.msg)
+      }
+    } catch (error) {
+      console.error('处理风险确认失败:', error)
+    }
+  }
+
+  // 关闭
+  const cancel = (data, type) => {
+    let closeDate = { ...data }
+    if (type) {
+      closeDate.downTime = 1
+    } else {
+      closeDate.downTime = 30
+    }
+
+    let financeRiskData = closeId.value.concat(closeDate)
+    let map = new Map()
+    for (let item of financeRiskData) {
+      map.set(item.id, item)
+    }
+    closeId.value = [...map.values()]
+    financeRisk.value = financeRisk.value.filter((item) => {
+      return !closeId.value.some((ele) => ele.id === item.id)
+    })
+  }
+
+  // 生命周期
+  onMounted(() => {
+    setTimeout(() => {
+      if (display.value['R-InternalTransfer-remindSingle']?.show) {
+        getFinanceRiskList()
+        beginTimer()
+      }
+    }, 3000)
+  })
+
+  onUnmounted(() => {
+    if (interval.value) {
+      clearInterval(interval.value)
+    }
+  })
+</script>
+
+<style scoped lang="scss">
+  .pageCainer {
+    height: 100%;
+    overflow: hidden;
+    position: relative;
+  }
+  .financeRisk {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 300px;
+    height: auto;
+    max-height: 100%;
+    z-index: 1;
+    .financeRisk_box {
+      background: #fff;
+      width: 100%;
+      height: auto;
+      padding: 10px;
+      box-sizing: border-box;
+      font-size: 14px;
+      margin: 10px 0;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+      border-radius: 6px;
+      .dialog-footer {
+        margin-top: 20px;
+        text-align: center;
+        .el-button {
+          min-width: 100px;
+        }
+      }
+    }
+  }
+</style>

+ 134 - 0
src/views/page/cachedRoute.ts

@@ -0,0 +1,134 @@
+// keepalive 组件
+// 需要缓存的组件名称列表
+export const cachedComponents = [
+  'ActiveConfig',
+  'ActivitiesApply',
+  'Activity',
+  'ActivityApplyTen',
+  'ActivityCustom',
+  'ActivityEnroll',
+  'AgencyTransfer',
+  'Agent',
+  'AgentApply',
+  'AgentTransfer',
+  'AttributionNumber',
+  'AutomaticBalanceAdjustmentGroup',
+  'AutomaticBalanceAdjustmentLogin',
+  'Bank',
+  'BelongingAdjust',
+  'BlackList',
+  'BusinessCard',
+  'CardOrder',
+  'CardType',
+  'CloseTradeGroup',
+  'CloseTradeLogin',
+  'CommissionAdjust',
+  'CommissionExtract',
+  'CommissionTemplate',
+  'CommissionTransfer',
+  'ConfigChanges',
+  'ConsumerShareLink',
+  'CooperativeAgent',
+  'CooperativeSubset',
+  'CooperativeUs',
+  'CopyTrade',
+  'Country',
+  'CustomerDynamic',
+  'CustomerList',
+  'DashBoard',
+  'DataReport',
+  'DeleteList',
+  'DepositWithdraw',
+  'DetailedInfo',
+  // 'DetailedInfoCid',
+  'DetailedInfoIbNo',
+  'DiscountCode',
+  'Email',
+  'EmailRecord',
+  'EmailRecords',
+  'EntryList',
+  'Exclude',
+  'FollowerTransfer',
+  'ForexmanActivityCustom',
+  'ForexmanActivityEnroll',
+  'GiftList',
+  'Global',
+  'GoogleSecretKey',
+  'Group',
+  'GroupHistory',
+  'GroupLeverage',
+  'GroupLeverageAdd',
+  'GroupType',
+  'HangUndo',
+  'HireList',
+  'ImportTradeAdd',
+  'ImportTradeDetails',
+  'ImportTradeList',
+  'ImportTradeSingleAdd',
+  'InternalTransfer',
+  'KYCReview',
+  'KycAuth',
+  'LeverAdjust',
+  'LimitedActivities',
+  'MassEmail',
+  'MonitoringReminder',
+  'NoticeInformation',
+  'OnlineDeposit',
+  'PammManagerHangUndo',
+  'PammManagerValid',
+  'PammPercentChange',
+  'PasswordReset',
+  'PaymentTransfer',
+  'PendingAccount',
+  'PotentialCustomer',
+  'PotentialInfoAdd',
+  'PromoCode',
+  'PushMessage',
+  'RateAdvance',
+  'Real',
+  'Rebate',
+  'Recharge',
+  'ReferralList',
+  'Refusal',
+  'ResignList',
+  'ReviewEmail',
+  'RevokeCreditGroup',
+  'RevokeCreditLogin',
+  'RewardDetails',
+  'Robot',
+  'Role',
+  'SalesList',
+  'SalesRewardStatistics',
+  'Simulation',
+  'Spread',
+  'Subscribe',
+  'Subset',
+  'SwapsAdd',
+  'SwapsDetails',
+  'SwapsList',
+  'SwapsSingleAdd',
+  'SynonymTransfer',
+  'Trading',
+  'Transactions',
+  'Unsubscribe',
+  'UploadDetails',
+  'UploadList',
+  'Us',
+  'UserOrder',
+  'Varieties',
+  'VerifiedInfoAdd',
+  'VerifiedUser',
+  'ViewCardSingle',
+  'VirtualCard',
+  'WebDetails',
+  'WebNotice',
+  'WebRecord',
+  'WithdrawalApplication',
+  'mail_Extension',
+  'mail_Source',
+  'review_Email',
+  'salaryPerformance',
+  'unsubscribe',
+  'Video',
+  'Info',
+]

+ 0 - 99
src/views/system/dept/components/deptDialog.vue

@@ -1,99 +0,0 @@
-<template>
-  <el-dialog v-model="dialogVisible" :title="title" width="50%" @close="close">
-    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
-      <el-form-item label="上级部门" prop="id">
-        <el-cascader
-          v-model="ruleForm.id"
-          :options="deptDataOptions"
-          :props="deptProps"
-          clearable
-          style="width: 100%"
-        />
-      </el-form-item>
-      <el-form-item label="部门名称" prop="deptName">
-        <el-input v-model="ruleForm.deptName" placeholder="请输入部门名称" />
-      </el-form-item>
-      <el-form-item label="状态">
-        <el-switch
-          v-model="ruleForm.status"
-          inline-prompt
-          active-text="启用"
-          inactive-text="禁用"
-        ></el-switch>
-      </el-form-item>
-      <el-form-item label="备注">
-        <el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入备注" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleConfim(ruleFormRef)">确定</el-button>
-      </span>
-    </template>
-  </el-dialog>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { reactive, ref } from 'vue'
-  import { deptData } from '@/mock/system'
-
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref<boolean>(false)
-  const deptDataOptions = ref(deptData)
-  const title = ref('新增部门')
-  const deptProps = {
-    value: 'id',
-    label: 'deptName',
-    checkStrictly: true,
-  }
-
-  const rules = reactive({
-    deptName: [{ required: true, message: '请输入部门名称', trigger: 'blur' }],
-    id: [{ required: true, message: '请选择上级部门', trigger: 'change' }],
-  })
-
-  const ruleForm = reactive({
-    deptName: '',
-    id: '',
-    remark: null,
-    status: true,
-  })
-
-  function close() {
-    ruleFormRef.value.resetFields()
-    Object.keys(ruleForm).forEach((key) => {
-      if (key === 'sex') ruleForm[key] = '男'
-      else if (key === 'status') ruleForm[key] = true
-      else ruleForm[key] = null
-    })
-  }
-
-  const show = (item = {}) => {
-    title.value = '新增部门'
-    if (item.deptName) {
-      title.value = '编辑部门'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const handleConfim = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        dialogVisible.value = false
-        ElMessage.success(`提交数据:${JSON.stringify(ruleForm)}`)
-        console.log('submit!', ruleForm)
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>
-<style lang="scss" scoped></style>

+ 0 - 41
src/views/system/dept/index.scss

@@ -1,41 +0,0 @@
-.header {
-  display: flex;
-  padding: 16px 16px 0px 16px;
-  margin-bottom: 16px;
-  border-radius: 4px;
-  background: white;
-  box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-}
-
-.footer {
-  flex: 1;
-  display: flex;
-  padding: 16px;
-  flex-direction: column;
-  border-radius: 4px;
-  overflow: hidden;
-  background: white;
-  box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-  position: relative;
-  box-sizing: border-box;
-
-  .util {
-    margin-bottom: 15px;
-    display: flex;
-    justify-content: flex-end;
-    flex-shrink: 0;
-  }
-
-  .table-inner {
-    flex: 1;
-    position: relative;
-  }
-
-  .table {
-    position: absolute;
-    left: 0;
-    top: 0;
-    width: 100%;
-    height: 100%;
-  }
-}

+ 0 - 130
src/views/system/dept/index.vue

@@ -1,130 +0,0 @@
-<template>
-  <div class="app-container">
-    <div class="header">
-      <el-form ref="ruleFormRef" :inline="true" :model="formInline">
-        <el-form-item label="部门名称" prop="username">
-          <el-input v-model="formInline.username" placeholder="请输入部门名称" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" :icon="Search" @click="onSubmit">查询</el-button>
-          <el-button @click="reset(ruleFormRef)">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-    <div class="footer">
-      <div class="util">
-        <el-button type="primary" @click="addHandler">
-          <el-icon><Plus /></el-icon>
-          新增部门
-        </el-button>
-      </div>
-      <div class="table-inner">
-        <el-table v-loading="loading" row-key="id" :data="tableData" style="width: 100%" border>
-          <el-table-column prop="deptName" label="部门名称" align="center" />
-          <el-table-column prop="status" label="状态" align="center">
-            <template #default="scope">
-              <el-switch
-                v-model="scope.row.status"
-                inline-prompt
-                active-text="启用"
-                inactive-text="禁用"
-                @change="changeStatus(scope.row)"
-              />
-            </template>
-          </el-table-column>
-          <el-table-column
-            prop="remark"
-            :show-overflow-tooltip="true"
-            width="300"
-            label="备注"
-            align="center"
-          />
-          <el-table-column prop="createTime" label="创建时间" align="center" width="180" />
-          <el-table-column prop="operator" label="操作" width="200px" align="center">
-            <template #default="scope">
-              <el-button type="primary" size="small" icon="Edit" @click="editHandler(scope.row)">
-                编辑
-              </el-button>
-              <el-button type="danger" size="small" icon="Delete" @click="del(scope.row)">
-                删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-    </div>
-    <DeptDialog ref="deptDialog" />
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ElMessageBox, FormInstance } from 'element-plus'
-  import { Search } from '@element-plus/icons-vue'
-  import { onMounted, reactive, ref } from 'vue'
-  import { deptData } from '@/mock/system'
-  import DeptDialog from './components/deptDialog.vue'
-
-  const tableData = ref(deptData)
-  const loading = ref(true)
-  const deptDialog = ref()
-  const ruleFormRef = ref<FormInstance>()
-  const formInline = reactive({})
-
-  onMounted(() => {
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  })
-
-  const onSubmit = () => {
-    console.log('submit!', formInline)
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  }
-
-  const reset = () => {
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  }
-
-  const addHandler = () => {
-    deptDialog.value.show()
-  }
-  const editHandler = (row) => {
-    deptDialog.value.show(row)
-  }
-
-  const del = () => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-  }
-  const changeStatus = (row) => {
-    ElMessageBox.confirm(
-      `确定要${!row.status ? '禁用' : '启用'} ${row.deptName} 账户吗?`,
-      '温馨提示',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-    )
-      .then(async () => {})
-      .catch(() => {
-        row.status = !row.status
-      })
-  }
-</script>
-
-<style scoped lang="scss">
-  @import './index';
-</style>

+ 0 - 117
src/views/system/dictionary/components/Side.vue

@@ -1,117 +0,0 @@
-<template>
-  <el-card class="m-dept-side">
-    <div class="title">字典管理</div>
-
-    <el-button type="primary" @click="addDictsort">
-      <el-icon color="#fff"><Plus /></el-icon>
-      <span style="margin-left: 8px">添加字典分类</span>
-    </el-button>
-
-    <el-input v-model="filterText" placeholder="输入关键字进行过滤" class="filter-search" />
-
-    <div class="filter-tree">
-      <el-scrollbar class="scrollbar">
-        <el-tree
-          ref="treeRef"
-          :data="tableData"
-          :props="defaultProps"
-          default-expand-all
-          :filter-node-method="filterNode"
-        >
-          <template #default="{ node, data }">
-            <span class="custom-tree-node" @click="selectAction(node, data)">
-              <span>{{ node.label }}</span>
-              <span v-if="data.id != null">
-                <el-button type="primary" link @click.stop="editDictsort(data)">编辑</el-button>
-                <el-button
-                  style="margin-left: 6px"
-                  type="danger"
-                  link
-                  @click.stop="remove(node, data)"
-                >
-                  删除
-                </el-button>
-              </span>
-            </span>
-          </template>
-        </el-tree>
-      </el-scrollbar>
-    </div>
-    <DictsortDialog ref="dictsortDialog" />
-  </el-card>
-</template>
-
-<script lang="ts" setup>
-  import { onBeforeMount, ref, watch } from 'vue'
-  import { ElMessageBox, ElTree } from 'element-plus'
-  import DictsortDialog from './dictsortDialog.vue'
-  import { dictionaryData } from '@/mock/system'
-
-  const emit = defineEmits(['change'])
-
-  const tableData = ref<Tree[]>(dictionaryData)
-  const dictsortDialog = ref(null)
-  interface Tree {
-    id: string
-    name: string
-    createTime?: string
-    remark?: string
-    children?: Tree[]
-  }
-
-  onBeforeMount(() => {
-    let allObj = { id: null, name: '全部' }
-    tableData.value = [allObj, ...dictionaryData]
-  })
-
-  const filterText = ref('')
-  const treeRef = ref<InstanceType<typeof ElTree>>()
-
-  const defaultProps = {
-    children: 'children',
-    label: 'name',
-    value: 'id',
-  }
-
-  // 监听输入
-  watch(filterText, (val) => {
-    treeRef.value!.filter(val)
-  })
-
-  // 搜索
-  const filterNode = (value: string, data: Tree) => {
-    console.log(data)
-    if (!value) return true
-    return data.name.includes(value)
-  }
-
-  const addDictsort = () => {
-    dictsortDialog.value.show()
-  }
-
-  const editDictsort = (item) => {
-    dictsortDialog.value.show(item)
-  }
-
-  const selectAction = (node, data) => {
-    emit('change', data)
-    console.log('node, data============', node, data)
-  }
-
-  const remove = (node, data) => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-
-    console.log('data===', node, data)
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import '../index.scss';
-</style>

+ 0 - 160
src/views/system/dictionary/components/Table.vue

@@ -1,160 +0,0 @@
-<template>
-  <div class="m-user-table">
-    <div class="header">
-      <el-form ref="ruleFormRef" :inline="true" :model="formInline">
-        <el-form-item label="名称" prop="username">
-          <el-input v-model="formInline.username" placeholder="请输入名称" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" :icon="Search" @click="onSubmit">查询</el-button>
-          <el-button @click="reset(ruleFormRef)">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-    <div class="footer">
-      <div class="util">
-        <el-button type="primary" @click="addHandler">
-          <el-icon><Plus /></el-icon>
-          新增字典项
-        </el-button>
-      </div>
-      <div class="table-inner">
-        <el-table v-loading="loading" :data="tableData" style="width: 100%; height: 100%" border>
-          <el-table-column prop="id" label="id" align="center" width="100" />
-          <el-table-column prop="name" label="名称" align="center" width="100" />
-          <el-table-column prop="key" label="键值" align="center" />
-          <el-table-column
-            prop="remark"
-            :show-overflow-tooltip="true"
-            width="180"
-            label="描述"
-            align="center"
-          />
-          <el-table-column prop="createTime" label="创建时间" align="center" width="180" />
-          <el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
-            <template #default="scope">
-              <el-button type="primary" size="small" icon="Edit" @click="editHandler(scope.row)">
-                编辑
-              </el-button>
-              <el-button type="danger" size="small" icon="Delete" @click="del(scope.row)">
-                删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-      <div class="pagination">
-        <el-pagination
-          v-model:current-page="currentPage1"
-          :page-size="10"
-          background
-          layout="total, sizes, prev, pager, next, jumper"
-          :total="1000"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
-      </div>
-    </div>
-
-    <DictionaryEntryDialog ref="dictionaryEntryDialog" />
-  </div>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { Search } from '@element-plus/icons-vue'
-  import { onMounted, reactive, ref } from 'vue'
-  import { dictionaryDetailData } from '@/mock/system'
-  import UserDialog from './userDialog.vue'
-  import DictionaryEntryDialog from './dictionaryEntryDialog.vue'
-
-  const tableData = ref(dictionaryDetailData[0].children)
-  const dialogVisible = ref(false)
-  const dictionaryEntryDialog = ref()
-  const ruleFormRef = ref<FormInstance>()
-  const formInline = reactive({})
-  const loading = ref(true)
-  const currentPage1 = ref(1)
-
-  const onSubmit = () => {
-    console.log('submit!', formInline)
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  }
-
-  const reset = (formEl: FormInstance | undefined) => {
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  }
-
-  const getList = (data) => {
-    loading.value = true
-    if (!data.id) {
-      tableData.value = []
-    } else {
-      let obj = dictionaryDetailData.find((item) => item.id === data.id)
-      tableData.value = obj.children
-    }
-
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  }
-
-  const addHandler = () => {
-    dictionaryEntryDialog.value.show()
-  }
-  const editHandler = (row) => {
-    dictionaryEntryDialog.value.show(row)
-  }
-
-  const del = (row) => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-  }
-  const changeStatus = (row) => {
-    ElMessageBox.confirm(
-      `确定要${!row.status ? '禁用' : '启用'} ${row.username} 账户吗?`,
-      '温馨提示',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-    )
-      .then(async () => {})
-      .catch(() => {
-        row.status = !row.status
-      })
-  }
-
-  const handleSizeChange = (val: number) => {
-    console.log(`${val} items per page`)
-  }
-
-  const handleCurrentChange = (val: number) => {
-    currentPage1.value = val
-  }
-
-  onMounted(() => {
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  })
-
-  defineExpose({
-    getList,
-  })
-</script>
-<style lang="scss" scoped>
-  @import '../index';
-</style>

+ 0 - 96
src/views/system/dictionary/components/dictionaryEntryDialog.vue

@@ -1,96 +0,0 @@
-<template>
-  <el-dialog v-model="dialogVisible" :title="title" width="50%" @close="close">
-    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
-      <el-form-item label="所属字典" prop="pid">
-        <el-cascader
-          v-model="ruleForm.pid"
-          style="width: 100%"
-          :options="dictionaryData"
-          :props="cascaderProps"
-          clearable
-        />
-      </el-form-item>
-      <el-form-item label="字典项名称" prop="name">
-        <el-input v-model="ruleForm.name" placeholder="请输入字典项名称" />
-      </el-form-item>
-      <el-form-item label="字典项键值" prop="key">
-        <el-input v-model="ruleForm.key" placeholder="请输入字典项键值" />
-      </el-form-item>
-      <el-form-item label="字典项描述" prop="remark">
-        <el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入字典项描述" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
-      </span>
-    </template>
-  </el-dialog>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { reactive, ref } from 'vue'
-
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref<boolean>(false)
-  const title = ref('新增字典项')
-  import { dictionaryData } from '@/mock/system'
-
-  const cascaderProps = {
-    value: 'id',
-    label: 'name',
-    checkStrictly: true,
-  }
-
-  const rules = reactive({
-    name: [
-      { required: true, message: '请输入名称', trigger: 'blur' },
-      { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
-    ],
-    key: [{ required: true, message: '请输入字典项键值', trigger: 'blur' }],
-    pid: [{ required: true, message: '请选择所属字典', trigger: 'change' }],
-  })
-
-  const ruleForm = reactive({
-    pid: '',
-    name: null,
-    remark: '',
-    key: null,
-  })
-
-  function close() {
-    ruleFormRef.value.resetFields()
-    Object.keys(ruleForm).forEach((key) => {
-      ruleForm[key] = null
-    })
-  }
-
-  const show = (item = {}) => {
-    console.log('======item=======', item)
-    title.value = '新增字典项'
-    if (item.pid) {
-      title.value = '编辑字典项'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const handleClose = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        dialogVisible.value = false
-        console.log('submit!', ruleForm)
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>
-<style lang="scss" scoped></style>

+ 0 - 95
src/views/system/dictionary/components/dictsortDialog.vue

@@ -1,95 +0,0 @@
-<template>
-  <el-dialog v-model="dialogVisible" :title="title" width="50%" @close="close">
-    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
-      <el-form-item label="父级字典">
-        <el-cascader
-          v-model="ruleForm.pid"
-          style="width: 100%"
-          :options="dictionaryData"
-          placeholder="请选择父级字典,默认为根字典"
-          :props="cascaderProps"
-          clearable
-        />
-      </el-form-item>
-      <el-form-item label="字典名称" prop="name">
-        <el-input v-model="ruleForm.name" placeholder="请输入字典项名称" />
-      </el-form-item>
-      <el-form-item label="字典编码" prop="keyCode">
-        <el-input v-model="ruleForm.keyCode" placeholder="请输入字典编码" />
-      </el-form-item>
-      <el-form-item label="字典描述" prop="remark">
-        <el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入字典项描述" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
-      </span>
-    </template>
-  </el-dialog>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { reactive, ref } from 'vue'
-
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref<boolean>(false)
-  const title = ref('新增字典项')
-  import { dictionaryData } from '@/mock/system'
-
-  const cascaderProps = {
-    value: 'id',
-    label: 'name',
-    checkStrictly: true,
-  }
-
-  const rules = reactive({
-    name: [
-      { required: true, message: '请输入名称', trigger: 'blur' },
-      { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
-    ],
-    keyCode: [{ required: true, message: '请输入字典编码', trigger: 'blur' }],
-  })
-
-  const ruleForm = reactive({
-    id: '',
-    name: null,
-    remark: '',
-    keyCode: null,
-  })
-
-  function close() {
-    ruleFormRef.value.resetFields()
-    Object.keys(ruleForm).forEach((key) => {
-      ruleForm[key] = null
-    })
-  }
-
-  const show = (item = {}) => {
-    title.value = '新增字典项'
-    if (item.id) {
-      title.value = '编辑字典项'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const handleClose = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        dialogVisible.value = false
-        console.log('submit!', ruleForm)
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>
-<style lang="scss" scoped></style>

+ 0 - 106
src/views/system/dictionary/index.scss

@@ -1,106 +0,0 @@
-.m-user {
-  display: flex;
-  flex-direction: row;
-}
-.m-user-table {
-  display: flex;
-  flex-direction: column;
-  flex: 1;
-  position: relative;
-  width: calc(100% - 230px);
-  .header {
-    display: flex;
-    padding: 16px 16px 0px 16px;
-    margin-bottom: 16px;
-    border-radius: 4px;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-  }
-  .footer {
-    flex: 1;
-    display: flex;
-    padding: 16px;
-    flex-direction: column;
-    border-radius: 4px;
-    overflow: hidden;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-    position: relative;
-    box-sizing: border-box;
-    .util {
-      margin-bottom: 15px;
-      display: flex;
-      justify-content: flex-end;
-      flex-shrink: 0;
-    }
-    .table-inner {
-      flex: 1;
-      position: relative;
-    }
-    .table {
-      position: absolute;
-      left: 0;
-      top: 0;
-      width: 100%;
-      height: 100%;
-    }
-  }
-  .pagination {
-    width: 100%;
-    display: flex;
-    justify-content: flex-end;
-    padding-top: 20px;
-    box-sizing: border-box;
-    flex-shrink: 0;
-  }
-}
-.custom-tree-node {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-
-.m-dept-side {
-  box-sizing: border-box;
-  width: 220px;
-  height: 100%;
-  padding: 18px;
-  margin-right: 10px;
-  flex-shrink: 0;
-  ::v-deep(.el-card__body) {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-    padding: 0 !important;
-    .el-tree-node__content {
-      height: 33px;
-    }
-    .el-tree {
-    }
-  }
-  .filter-search {
-    flex-shrink: 0;
-    margin-bottom: 10px;
-    margin-top: 10px;
-  }
-  .title {
-    flex-shrink: 0;
-    margin: 0 0 15px;
-    font-size: 18px;
-    font-weight: 700;
-  }
-  .scrollbar {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    overflow: auto;
-  }
-  .filter-tree {
-    flex: 1;
-    overflow: hidden;
-    position: relative;
-  }
-}

+ 0 - 22
src/views/system/dictionary/index.vue

@@ -1,22 +0,0 @@
-<template>
-  <div class="app-container m-user">
-    <Side @change="changeAction" />
-    <Table ref="table" />
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import Table from './components/Table.vue'
-  import Side from './components/Side.vue'
-  import { ref } from 'vue'
-
-  const table = ref()
-
-  const changeAction = (data) => {
-    table.value.getList(data)
-  }
-</script>
-
-<style scoped lang="scss">
-  @import './index';
-</style>

+ 0 - 84
src/views/system/menu/components/MenuDrawer.vue

@@ -1,84 +0,0 @@
-<template>
-  <div>
-    <el-drawer v-model="dialogVisible" :title="title" size="50%">
-      <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="120px">
-        <el-form-item label="菜单类型" prop="menuType">
-          <el-radio-group v-model="ruleForm.menuType">
-            <el-radio-button label="目录" />
-            <el-radio-button label="菜单" />
-            <el-radio-button label="按钮" />
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="菜单名称" prop="menuName">
-          <el-input v-model="ruleForm.menuName" placeholder="请输入菜单名称" />
-        </el-form-item>
-        <el-form-item label="父级菜单" prop="role">
-          <el-cascader style="width: 100%" :options="menuData" :props="cascaderProps" clearable />
-        </el-form-item>
-        <el-form-item label="权限标识" prop="identification">
-          <el-input v-model="ruleForm.identification" placeholder="请输入权限标识" />
-        </el-form-item>
-        <el-form-item label="路由地址" prop="identification">
-          <el-input v-model="ruleForm.url" placeholder="请输入路由地址" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
-        </span>
-      </template>
-    </el-drawer>
-  </div>
-</template>
-<script lang="ts" setup>
-  import { reactive, ref } from 'vue'
-  import { FormInstance } from 'element-plus'
-  import { menuData } from '@/mock/system'
-
-  const tableData = ref(menuData)
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref()
-  const rules = reactive({
-    roleName: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
-    roleIdentification: [{ required: true, message: '请输入角色标识', trigger: 'blur' }],
-  })
-  const title = ref('新增菜单')
-  const ruleForm = reactive({
-    name: '',
-    roleIdentification: '',
-    describe: null,
-    status: true,
-  })
-  const cascaderProps = {
-    value: 'menuName',
-    label: 'menuName',
-    checkStrictly: true,
-  }
-
-  const show = (item = {}) => {
-    title.value = '新增菜单'
-    if (item.name) {
-      title.value = '编辑菜单'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const handleClose = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        console.log('submit!')
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>
-<style></style>

+ 0 - 133
src/views/system/menu/index.vue

@@ -1,133 +0,0 @@
-<template>
-  <div class="app-container">
-    <div class="header">
-      <el-form ref="ruleFormRef" :inline="true" :model="formInline">
-        <el-form-item label="菜单名称" prop="username">
-          <el-input v-model="formInline.username" placeholder="请输入菜单名称" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" :icon="Search" @click="onSubmit">查询</el-button>
-          <el-button @click="reset(ruleFormRef)">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-    <div class="footer">
-      <div class="util">
-        <el-button type="primary" @click="add">
-          <el-icon><Plus /></el-icon>
-          新增菜单
-        </el-button>
-      </div>
-      <div class="table-wrap">
-        <el-table
-          :data="tableData"
-          style="width: 100%"
-          border
-          default-expand-all
-          row-key="id"
-          class="table"
-        >
-          <el-table-column prop="menuName" label="权限名称" />
-          <el-table-column prop="menuType" label="权限类型" />
-          <el-table-column prop="menuRouter" label="权限路由" />
-          <el-table-column prop="identification" label="权限标识" />
-          <el-table-column prop="status" label="操作">
-            <template #default="scope">
-              <el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
-                编辑
-              </el-button>
-              <el-button type="danger" size="small" icon="Delete" @click="del(scope.row)">
-                删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-    </div>
-    <MenuDrawer ref="menuDrawerRef" />
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ElMessageBox, FormInstance } from 'element-plus'
-  import { reactive, ref } from 'vue'
-  import { Search } from '@element-plus/icons-vue'
-  import { menuData } from '@/mock/system'
-  import MenuDrawer from './components/MenuDrawer.vue'
-
-  const tableData = ref(menuData)
-  const title = ref('新增')
-  const menuDrawerRef = ref()
-  const ruleFormRef = ref<FormInstance>()
-  const formInline = reactive({})
-  const reset = (formEl: FormInstance | undefined) => {}
-
-  const onSubmit = () => {
-    console.log('submit!', formInline)
-  }
-
-  const add = () => {
-    menuDrawerRef.value.show()
-  }
-
-  const edit = (row) => {
-    menuDrawerRef.value.show(row)
-  }
-  const del = () => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-  }
-</script>
-
-<style scoped lang="scss">
-  .header {
-    display: flex;
-    padding: 16px 16px 0px 16px;
-    margin-bottom: 16px;
-    border-radius: 4px;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-  }
-  .footer {
-    flex: 1;
-    display: flex;
-    padding: 16px;
-    flex-direction: column;
-    border-radius: 4px;
-    overflow: hidden;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-    position: relative;
-    box-sizing: border-box;
-    .util {
-      margin-bottom: 15px;
-      display: flex;
-      justify-content: flex-end;
-      flex-shrink: 0;
-    }
-    .table-wrap {
-      flex: 1;
-      display: flex;
-      position: relative;
-      overflow: hidden;
-    }
-    .table-inner {
-      width: 100%;
-      height: 100%;
-      position: relative;
-    }
-    .table {
-      position: absolute;
-      left: 0;
-      top: 0;
-      width: 100%;
-      height: 100%;
-    }
-  }
-</style>

+ 0 - 90
src/views/system/role/components/roleDrawer.vue

@@ -1,90 +0,0 @@
-<template>
-  <el-drawer v-model="dialogVisible" :title="title" size="50%" @close="close">
-    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
-      <el-form-item label="角色名称" prop="roleName">
-        <el-input v-model="ruleForm.roleName" placeholder="请输入角色名称" />
-      </el-form-item>
-      <el-form-item label="角色标识" prop="roleIdentification">
-        <el-input v-model="ruleForm.roleIdentification" placeholder="请输入角色标识" />
-      </el-form-item>
-      <el-form-item label="角色状态">
-        <el-switch
-          v-model="ruleForm.status"
-          inline-prompt
-          active-text="启用"
-          inactive-text="禁用"
-        ></el-switch>
-      </el-form-item>
-      <el-form-item label="角色描述">
-        <el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入角色描述" />
-      </el-form-item>
-      <el-form-item label="菜单权限">
-        <el-tree :data="menuData" show-checkbox node-key="id" :props="defaultProps" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
-      </span>
-    </template>
-  </el-drawer>
-</template>
-<script lang="ts" setup>
-  import { reactive, ref } from 'vue'
-  import { FormInstance } from 'element-plus'
-  import { menuData } from '@/mock/system'
-
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref(false)
-  const title = ref('新增角色')
-  const ruleForm = reactive({
-    roleName: '',
-    roleIdentification: '',
-    describe: null,
-    status: true,
-  })
-  const defaultProps = {
-    children: 'children',
-    label: 'menuName',
-  }
-
-  function close() {
-    ruleFormRef.value.resetFields()
-    Object.keys(ruleForm).forEach((key) => {
-      if (key === 'status') ruleForm[key] = true
-      else ruleForm[key] = null
-    })
-  }
-
-  const show = (item = {}) => {
-    title.value = '新增角色'
-    if (item.roleName) {
-      title.value = '编辑角色'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const rules = reactive({
-    roleName: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
-    roleIdentification: [{ required: true, message: '请输入角色标识', trigger: 'blur' }],
-  })
-
-  const handleClose = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        dialogVisible.value = false
-        console.log('submit!', obj)
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>

+ 0 - 168
src/views/system/role/index.vue

@@ -1,168 +0,0 @@
-<template>
-  <div class="app-container">
-    <div class="header">
-      <el-form ref="ruleFormRef" :inline="true" :model="formInline" class="demo-form-inline">
-        <el-form-item label="角色名称" prop="roleName">
-          <el-input v-model="formInline.roleName" placeholder="请输入角色名称" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" :icon="Search" @click="onSubmit">查询</el-button>
-          <el-button @click="reset(ruleFormRef)">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-    <div class="footer">
-      <div class="util">
-        <el-button type="primary" @click="add">
-          <el-icon><Plus /></el-icon>
-          新增角色
-        </el-button>
-      </div>
-      <div class="table-inner">
-        <el-table v-loading="loading" :data="tableData" style="width: 100%" border>
-          <el-table-column prop="roleName" label="角色名称" />
-          <el-table-column prop="roleIdentification" label="角色标识" />
-          <el-table-column prop="status" label="角色状态" align="center">
-            <template #default="scope">
-              <el-switch
-                v-model="scope.row.status"
-                inline-prompt
-                active-text="启用"
-                inactive-text="禁用"
-                @change="changeStatus(scope.row)"
-              />
-            </template>
-          </el-table-column>
-          <el-table-column
-            prop="describe"
-            :show-overflow-tooltip="true"
-            width="180"
-            label="角色描述"
-          />
-          <el-table-column prop="createTime" label="创建时间" />
-          <el-table-column prop="status" label="操作" width="180">
-            <template #default="scope">
-              <el-button type="primary" size="small" icon="Edit" @click="edit(scope.row)">
-                编辑
-              </el-button>
-              <el-button type="danger" size="small" icon="Delete" @click="del(scope.row)">
-                删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-    </div>
-    <RoleDrawer ref="roleDrawer" />
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { onMounted, reactive, ref } from 'vue'
-  import { Search } from '@element-plus/icons-vue'
-  import RoleDrawer from './components/roleDrawer.vue'
-  import { roleData } from '@/mock/system'
-  const tableData = ref(roleData)
-
-  const loading = ref(true)
-  const roleDrawer = ref()
-  const formSize = ref('default')
-  const ruleFormRef = ref<FormInstance>()
-  const formInline = reactive({})
-
-  const onSubmit = () => {
-    console.log('submit!', formInline)
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  }
-
-  const reset = (formEl: FormInstance | undefined) => {
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  }
-  const add = () => {
-    roleDrawer.value.show()
-  }
-
-  const edit = (row) => {
-    roleDrawer.value.show(row)
-  }
-
-  const del = (row) => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-  }
-
-  const changeStatus = (row) => {
-    ElMessageBox.confirm(
-      `确定要${!row.status ? '禁用' : '启用'} ${row.roleName} 角色吗?`,
-      '温馨提示',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-    )
-      .then(async () => {})
-      .catch(() => {
-        row.status = !row.status
-      })
-  }
-
-  onMounted(() => {
-    setTimeout(() => {
-      loading.value = false
-    }, 500)
-  })
-</script>
-
-<style scoped lang="scss">
-  .header {
-    display: flex;
-    padding: 16px 16px 0px 16px;
-    margin-bottom: 16px;
-    border-radius: 4px;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-  }
-  .footer {
-    flex: 1;
-    display: flex;
-    padding: 16px;
-    flex-direction: column;
-    border-radius: 4px;
-    overflow: hidden;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-    position: relative;
-    box-sizing: border-box;
-    .util {
-      margin-bottom: 15px;
-      display: flex;
-      justify-content: flex-end;
-      flex-shrink: 0;
-    }
-    .table-inner {
-      flex: 1;
-      position: relative;
-    }
-    .table {
-      position: absolute;
-      left: 0;
-      top: 0;
-      width: 100%;
-      height: 100%;
-    }
-  }
-</style>

+ 0 - 118
src/views/system/user/components/userDialog.vue

@@ -1,118 +0,0 @@
-<template>
-  <el-dialog v-model="dialogVisible" :title="title" width="50%" @close="close">
-    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
-      <el-form-item label="用户名" prop="username">
-        <el-input v-model="ruleForm.username" placeholder="请输入用户名" />
-      </el-form-item>
-      <el-form-item label="昵称" prop="nickname">
-        <el-input v-model="ruleForm.nickname" placeholder="请输入昵称" />
-      </el-form-item>
-      <el-form-item label="性别" prop="sex">
-        <el-radio-group v-model="ruleForm.sex">
-          <el-radio label="男">男</el-radio>
-          <el-radio label="女">女</el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="关联角色" prop="role">
-        <el-select v-model="ruleForm.role" placeholder="请选择" style="width: 100%">
-          <el-option :key="0" label="超级管理员" value="超级管理员"></el-option>
-          <el-option :key="1" label="管理员" value="管理员"></el-option>
-          <el-option :key="2" label="普通用户" value="普通用户"></el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="手机号" prop="photo">
-        <el-input v-model="ruleForm.photo" placeholder="请输入手机号" />
-      </el-form-item>
-      <el-form-item label="账户密码">
-        <el-input
-          v-model="ruleForm.password"
-          placeholder="请输入账户密码,如果不输入默认123456"
-          type="password"
-          clearable
-        />
-      </el-form-item>
-      <el-form-item label="用户状态">
-        <el-switch
-          v-model="ruleForm.status"
-          inline-prompt
-          active-text="启用"
-          inactive-text="禁用"
-        ></el-switch>
-      </el-form-item>
-      <el-form-item label="用户描述" prop="describe">
-        <el-input v-model="ruleForm.describe" type="textarea" placeholder="请输入用户描述" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
-      </span>
-    </template>
-  </el-dialog>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { reactive, ref } from 'vue'
-
-  const ruleFormRef = ref<FormInstance>()
-  const dialogVisible = ref<boolean>(false)
-  const title = ref('新增用户')
-
-  const rules = reactive({
-    nickname: [
-      { required: true, message: '请输入昵称', trigger: 'blur' },
-      { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
-    ],
-    username: [{ required: true, message: '请输入', trigger: 'blur' }],
-    role: [{ required: true, message: '请选择角色', trigger: 'change' }],
-    sex: [{ required: true, message: '请选择性别', trigger: 'change' }],
-  })
-
-  const ruleForm = reactive({
-    username: '',
-    nickname: null,
-    sex: '男',
-    role: null,
-    photo: null,
-    password: null,
-    describe: null,
-    status: true,
-  })
-
-  function close() {
-    ruleFormRef.value.resetFields()
-    Object.keys(ruleForm).forEach((key) => {
-      if (key === 'sex') ruleForm[key] = '男'
-      else if (key === 'status') ruleForm[key] = true
-      else ruleForm[key] = null
-    })
-  }
-
-  const show = (item = {}) => {
-    title.value = '新增用户'
-    if (item.username) {
-      title.value = '编辑用户'
-      Object.keys(item).forEach((key) => {
-        ruleForm[key] = item[key]
-      })
-    }
-    dialogVisible.value = true
-  }
-
-  const handleClose = async (done: () => void) => {
-    await ruleFormRef.value.validate((valid, fields) => {
-      if (valid) {
-        dialogVisible.value = false
-        console.log('submit!', ruleForm)
-      } else {
-        console.log('error submit!', fields)
-      }
-    })
-  }
-
-  defineExpose({
-    show,
-  })
-</script>
-<style lang="scss" scoped></style>

+ 0 - 64
src/views/system/user/components/userSide.vue

@@ -1,64 +0,0 @@
-<template>
-  <el-card class="m-dept-side">
-    <div class="title">部门列表</div>
-    <el-input v-model="filterText" placeholder="输入关键字进行过滤" class="filter-search" />
-    <div class="filter-tree">
-      <el-scrollbar class="scrollbar">
-        <el-tree
-          ref="treeRef"
-          :data="tableData"
-          :props="defaultProps"
-          default-expand-all
-          :filter-node-method="filterNode"
-        />
-      </el-scrollbar>
-    </div>
-  </el-card>
-</template>
-
-<script lang="ts" setup>
-  import { onBeforeMount, ref, watch } from 'vue'
-  import { ElTree } from 'element-plus'
-  import { deptData } from '@/mock/system'
-
-  const tableData = ref<Tree[]>(deptData)
-  interface Tree {
-    id: string
-    deptName: string
-    orderNo: number
-    createTime?: string
-    remark?: string
-    status?: boolean
-    children?: Tree[]
-  }
-
-  onBeforeMount(() => {
-    let allObj = { id: '', deptName: '全部' }
-    tableData.value = [allObj, ...deptData]
-  })
-
-  const filterText = ref('')
-  const treeRef = ref<InstanceType<typeof ElTree>>()
-
-  const defaultProps = {
-    children: 'children',
-    label: 'deptName',
-    value: 'id',
-  }
-
-  // 监听输入
-  watch(filterText, (val) => {
-    treeRef.value!.filter(val)
-  })
-
-  // 搜索
-  const filterNode = (value: string, data: Tree) => {
-    console.log(data)
-    if (!value) return true
-    return data.deptName.includes(value)
-  }
-</script>
-
-<style lang="scss" scoped>
-  @import '../index.scss';
-</style>

+ 0 - 154
src/views/system/user/components/userTable.vue

@@ -1,154 +0,0 @@
-<template>
-  <div class="m-user-table">
-    <div class="header">
-      <el-form ref="ruleFormRef" :inline="true" :model="formInline">
-        <el-form-item label="用户名" prop="username">
-          <el-input v-model="formInline.username" placeholder="请输入用户名" />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" :icon="Search" @click="onSubmit">查询</el-button>
-          <el-button @click="reset(ruleFormRef)">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </div>
-    <div class="footer">
-      <div class="util">
-        <el-button type="primary" @click="addHandler">
-          <el-icon><Plus /></el-icon>
-          新增用户
-        </el-button>
-      </div>
-      <div class="table-inner">
-        <el-table v-loading="loading" :data="tableData" style="width: 100%; height: 100%" border>
-          <el-table-column prop="username" label="用户名" align="center" width="100" />
-          <el-table-column prop="nickname" label="昵称" align="center" />
-          <el-table-column prop="sex" label="性别" align="center" />
-          <el-table-column prop="role" label="关联角色" align="center" width="120" />
-          <el-table-column prop="photo" label="手机号" align="center" width="120" />
-          <el-table-column prop="status" label="用户状态" align="center">
-            <template #default="scope">
-              <el-switch
-                v-model="scope.row.status"
-                inline-prompt
-                active-text="启用"
-                inactive-text="禁用"
-                @change="changeStatus(scope.row)"
-              />
-            </template>
-          </el-table-column>
-          <el-table-column
-            prop="describe"
-            :show-overflow-tooltip="true"
-            width="180"
-            label="用户描述"
-            align="center"
-          />
-          <el-table-column prop="createTime" label="创建时间" align="center" width="180" />
-          <el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
-            <template #default="scope">
-              <el-button type="primary" size="small" icon="Edit" @click="editHandler(scope.row)">
-                编辑
-              </el-button>
-              <el-button type="danger" size="small" icon="Delete" @click="del(scope.row)">
-                删除
-              </el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-      <div class="pagination">
-        <el-pagination
-          v-model:current-page="currentPage1"
-          :page-size="10"
-          background
-          layout="total, sizes, prev, pager, next, jumper"
-          :total="1000"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
-      </div>
-    </div>
-
-    <UserDialog ref="userDialog" />
-  </div>
-</template>
-<script lang="ts" setup>
-  import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
-  import { Search } from '@element-plus/icons-vue'
-  import { onMounted, reactive, ref } from 'vue'
-  import { userData } from '@/mock/system'
-  import UserDialog from './userDialog.vue'
-
-  const tableData = ref(userData)
-  const dialogVisible = ref(false)
-  const userDialog = ref()
-  const ruleFormRef = ref<FormInstance>()
-  const formInline = reactive({})
-  const loading = ref(true)
-  const currentPage1 = ref(1)
-
-  const onSubmit = () => {
-    console.log('submit!', formInline)
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  }
-
-  const reset = (formEl: FormInstance | undefined) => {
-    loading.value = true
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  }
-
-  const addHandler = () => {
-    userDialog.value.show()
-  }
-  const editHandler = (row) => {
-    userDialog.value.show(row)
-  }
-
-  const del = (row) => {
-    ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-      draggable: true,
-    })
-      .then(() => {})
-      .catch(() => {})
-  }
-  const changeStatus = (row) => {
-    ElMessageBox.confirm(
-      `确定要${!row.status ? '禁用' : '启用'} ${row.username} 账户吗?`,
-      '温馨提示',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-    )
-      .then(async () => {})
-      .catch(() => {
-        row.status = !row.status
-      })
-  }
-
-  const handleSizeChange = (val: number) => {
-    console.log(`${val} items per page`)
-  }
-
-  const handleCurrentChange = (val: number) => {
-    currentPage1.value = val
-  }
-
-  onMounted(() => {
-    setTimeout(() => {
-      loading.value = false
-    }, 1000)
-  })
-</script>
-<style lang="scss" scoped>
-  @import '../index';
-</style>

+ 0 - 99
src/views/system/user/index.scss

@@ -1,99 +0,0 @@
-.m-user {
-  display: flex;
-  flex-direction: row;
-}
-.m-user-table {
-  display: flex;
-  flex-direction: column;
-  flex: 1;
-  position: relative;
-  width: calc(100% - 230px);
-  .header {
-    display: flex;
-    padding: 16px 16px 0px 16px;
-    margin-bottom: 16px;
-    border-radius: 4px;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-  }
-  .footer {
-    flex: 1;
-    display: flex;
-    padding: 16px;
-    flex-direction: column;
-    border-radius: 4px;
-    overflow: hidden;
-    background: white;
-    box-shadow: 0 0 12px rgb(0 0 0 / 5%);
-    position: relative;
-    box-sizing: border-box;
-    .util {
-      margin-bottom: 15px;
-      display: flex;
-      justify-content: flex-end;
-      flex-shrink: 0;
-    }
-    .table-inner {
-      flex: 1;
-      position: relative;
-    }
-    .table {
-      position: absolute;
-      left: 0;
-      top: 0;
-      width: 100%;
-      height: 100%;
-    }
-  }
-  .pagination {
-    width: 100%;
-    display: flex;
-    justify-content: flex-end;
-    padding-top: 20px;
-    box-sizing: border-box;
-    flex-shrink: 0;
-  }
-}
-
-.m-dept-side {
-  box-sizing: border-box;
-  width: 220px;
-  height: 100%;
-  padding: 18px;
-  margin-right: 10px;
-  flex-shrink: 0;
-  ::v-deep(.el-card__body) {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-    padding: 0 !important;
-    .el-tree-node__content {
-      height: 33px;
-    }
-    .el-tree {
-    }
-  }
-  .filter-search {
-    flex-shrink: 0;
-    margin-bottom: 10px;
-  }
-  .title {
-    flex-shrink: 0;
-    margin: 0 0 15px;
-    font-size: 18px;
-    font-weight: 700;
-  }
-  .scrollbar {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    overflow: auto;
-  }
-  .filter-tree {
-    flex: 1;
-    overflow: hidden;
-    position: relative;
-  }
-}

+ 0 - 15
src/views/system/user/index.vue

@@ -1,15 +0,0 @@
-<template>
-  <div class="app-container m-user">
-    <UserSide />
-    <UserTable />
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import UserTable from './components/userTable.vue'
-  import UserSide from './components/userSide.vue'
-</script>
-
-<style scoped lang="scss">
-  @import './index';
-</style>