li.shaoyi 3 năm trước cách đây
mục cha
commit
710853708b
50 tập tin đã thay đổi với 1100 bổ sung497 xóa
  1. 71 0
      src/business/account/index.ts
  2. 9 5
      src/business/common/index.ts
  3. 6 9
      src/business/goods/index.ts
  4. 8 2
      src/business/menu/index.ts
  5. 2 2
      src/business/sign/index.ts
  6. 30 0
      src/business/table/index.ts
  7. 12 1
      src/constants/enum/diamond.ts
  8. 5 5
      src/constants/enum/index.ts
  9. 9 9
      src/hooks/auth/index.ts
  10. 2 2
      src/hooks/echarts/candlestick/options.ts
  11. 2 2
      src/hooks/echarts/timeline/options.ts
  12. 7 7
      src/mock/router.ts
  13. 3 3
      src/packages/mobile/router/index.ts
  14. 4 4
      src/packages/mobile/views/home/components/market/index.vue
  15. 2 2
      src/packages/pc/App.vue
  16. 4 4
      src/packages/pc/components/layouts/sidebar/index.less
  17. 4 4
      src/packages/pc/components/layouts/sidebar/index.vue
  18. 15 8
      src/packages/pc/components/layouts/sidemenu/index.vue
  19. 13 1
      src/packages/pc/components/layouts/sidemenu/submenu.vue
  20. 1 1
      src/packages/pc/components/modules/auth-component/index.vue
  21. 1 1
      src/packages/pc/components/modules/auth-operation/index.vue
  22. 6 22
      src/packages/pc/router/dynamicRouter.ts
  23. 5 9
      src/packages/pc/router/index.ts
  24. 21 1
      src/packages/pc/views/mine/account/index.vue
  25. 25 1
      src/packages/pc/views/mine/info/index.vue
  26. 0 0
      src/packages/pc/views/search/diamond/index.vue
  27. 0 0
      src/packages/pc/views/search/diamonds/index.vue
  28. 3 0
      src/packages/pc/views/system/menu/components/edit/index.vue
  29. 1 1
      src/packages/pc/views/system/menu/index.vue
  30. 3 3
      src/packages/pc/views/system/role/components/auth/index.vue
  31. 1 1
      src/packages/pc/views/system/role/index.vue
  32. 6 0
      src/packages/pc/views/warehousing/stock/components/edit/diamond.vue
  33. 214 0
      src/packages/pc/views/warehousing/stock/components/edit/diamonds.vue
  34. 6 0
      src/packages/pc/views/warehousing/stock/components/edit/fancy.vue
  35. 24 201
      src/packages/pc/views/warehousing/stock/components/edit/index.vue
  36. 6 0
      src/packages/pc/views/warehousing/stock/components/edit/jewelry.vue
  37. 6 0
      src/packages/pc/views/warehousing/stock/components/edit/rough.vue
  38. 17 3
      src/services/api/account/index.ts
  39. 2 2
      src/services/http/index.ts
  40. 3 3
      src/services/language/index.ts
  41. 3 4
      src/services/socket/quote/index.ts
  42. 2 2
      src/services/socket/trade/index.ts
  43. 5 3
      src/stores/index.ts
  44. 2 2
      src/stores/modules/account.ts
  45. 37 0
      src/stores/modules/common.ts
  46. 2 2
      src/stores/modules/futures.ts
  47. 95 106
      src/stores/modules/storage.ts
  48. 321 5
      src/types/ermcp/account.d.ts
  49. 4 20
      src/types/store/globalStorage.d.ts
  50. 70 34
      src/utils/storage/index.ts

+ 71 - 0
src/business/account/index.ts

@@ -0,0 +1,71 @@
+import { shallowRef, computed } from 'vue'
+import { useDataTable } from '@/hooks/datatable'
+import { commonStore, accountStore, sessionData } from '@/stores'
+import { queryAccountInOutApply } from '@/services/api/account'
+import { useTableColumns } from '../table'
+
+export function useAccount() {
+    const { getLoginDataInfo } = commonStore
+    const { accountList } = accountStore
+
+    const accountInfo = computed(() => accountList.value[0])
+    const userInfo = getLoginDataInfo('userInfo')
+
+    return {
+        userInfo,
+        accountInfo,
+    }
+}
+
+/**
+ * 账户出入金
+ * @returns 
+ */
+export function useAccountInOut() {
+    const { dataList, selectList, buttonList } = useDataTable<Ermcp.AccountOutInApplyRsp>()
+    const { columns } = useTableColumns('table_pcweb_qhj_recharge_review')
+    const loading = shallowRef(false)
+
+    selectList.value = [
+        {
+            key: 'applystatus',
+            label: '状态',
+            options: [
+                { label: '待审核', value: 1 },
+                { label: '待复审', value: 2 }
+            ],
+        },
+        {
+            key: 'userinfotype',
+            label: '申请类型',
+            options: [
+                { label: '入金', value: 1 },
+                { label: '出金', value: 2 }
+            ],
+        },
+    ]
+
+    const getAccountInOutApply = () => {
+        loading.value = true
+        return queryAccountInOutApply({
+            data: {
+                userid: sessionData.getLoginInfo('UserID')
+            },
+            success: (res) => {
+                dataList.value = res.data
+            },
+            complete: () => {
+                loading.value = false
+            }
+        })
+    }
+
+    return {
+        loading,
+        dataList,
+        selectList,
+        buttonList,
+        columns,
+        getAccountInOutApply,
+    }
+}

+ 9 - 5
src/business/common/index.ts

@@ -1,5 +1,5 @@
 import { timerTask } from '@/utils/timer'
-import { storageData, futuresStore, resetStore, accountStore } from '@/stores'
+import { commonStore, sessionData, futuresStore, resetStore, accountStore } from '@/stores'
 import { tokenCheck } from '@/services/api/account'
 import service from '@/services'
 import eventBus from '@/services/bus'
@@ -31,14 +31,17 @@ export const business = new (class {
  */
 export async function initBaseData(callback?: () => void) {
     await service.onReady(async () => {
-        if (storageData.getLoginInfo('Token')) {
+        if (sessionData.getLoginInfo('Token')) {
             // 连接交易服务
             socket.connectTrade()
             await checkToken()
 
             const asyncTask = [
+                commonStore.getLoginData(),
                 futuresStore.getGoodsList(),
-                storageData.init(),
+                sessionData.getUserMenuList(),
+                sessionData.getAllEnumList(),
+                sessionData.getTableColumnList(),
             ]
 
             await Promise.all(asyncTask).then(() => {
@@ -57,10 +60,11 @@ export async function initBaseData(callback?: () => void) {
  * Token 效验
  */
 export function checkToken() {
+    const { LoginID, Token } = sessionData.getValue('loginInfo')
     return tokenCheck({
         data: {
-            LoginID: storageData.getLoginInfo('LoginID'),
-            Token: storageData.getLoginInfo('Token'),
+            LoginID,
+            Token,
         },
         fail: () => {
             // 失败应该退到登录页面

+ 6 - 9
src/business/goods/index.ts

@@ -4,7 +4,7 @@ import { formatDecimal } from '@/filters'
 import { queryWarehouseInfo } from '@/services/api/common'
 import { addZSGoods } from '@/services/api/goods'
 import {
-    getCategoryList,
+    Category,
     getCurrencyTypeList,
     getShapeTypeList,
     getColorTypeList,
@@ -15,19 +15,19 @@ import {
     getFluorescenceTypeList,
     getCurrencyTypeInfo,
 } from '@/constants/enum/diamond'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 
-export function useGoodsForm() {
-    const loginUserId = storageData.getLoginInfo('UserID')
-    const loginAccountId = storageData.getLoginInfo('AccountIDs')[0]
+export function useGoodsForm(category: Category) {
+    const loginUserId = sessionData.getLoginInfo('UserID')
+    const loginAccountId = sessionData.getLoginInfo('AccountIDs')[0]
     const loading = shallowRef(false)
     const warehouseList = shallowRef<Ermcp.WarehouseInfoRsp[]>([]) // 仓库列表
     const formItem = reactive<Proto.GZWRStandardExInfo>({
+        ZSCategory: category,
         GoodsNo: '',
     })
 
     const enums = {
-        categoryList: getCategoryList(),
         currencyTypeList: getCurrencyTypeList(),
         shapeTypeList: getShapeTypeList(),
         colorTypeList: getColorTypeList(),
@@ -38,9 +38,6 @@ export function useGoodsForm() {
         fluorescenceTypeList: getFluorescenceTypeList(),
     }
 
-    if (enums.categoryList.length) {
-        formItem.ZSCategory = enums.categoryList[0].value
-    }
     if (enums.currencyTypeList.length) {
         formItem.ZSCurrencyType = enums.currencyTypeList[0].value
     }

+ 8 - 2
src/business/menu/index.ts

@@ -35,12 +35,17 @@ export function useMenu() {
 
     // 扁平列表树形化
     const arrayToTree = (list: Ermcp.NewFuncmenuRsp[]) => {
-        return list.filter((item) => {
+        return list.filter(<T extends Ermcp.NewFuncmenuRsp>(item: T) => {
+            for (const key in item) {
+                if (item[key] == null) {
+                    delete item[key] // 删除无效值
+                }
+            }
             const children = list.filter((e) => e.parentcode === item.resourcecode) // 找出所有子元素
             if (children.length) {
                 item.children = children // 递归向父级添加子元素
             }
-            return item.parentcode === null // 非父级的不返回
+            return !item.parentcode // 非父级的不返回
         })
     }
 
@@ -72,6 +77,7 @@ export function useMenuForm(selectedRow?: Ermcp.NewFuncmenuRsp) {
     const loading = ref(false)
     const formItem = reactive<Partial<Ermcp.NewFuncmenuRsp>>({
         menutype: 5,
+        parentcode: '',
         ...selectedRow
     })
 

+ 2 - 2
src/business/sign/index.ts

@@ -1,6 +1,6 @@
 import { ref, reactive } from 'vue'
 import { v4 } from 'uuid'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import { initBaseData } from '@/business/common'
 import { login, queryLoginId } from '@/services/api/account'
 import cryptojs from 'crypto-js'
@@ -39,7 +39,7 @@ export function useSign() {
                             LoginPWD: cryptojs.SHA256(res.data + user.LoginPWD).toString(),
                         },
                         success: (res) => {
-                            storageData.setLoginInfo(res);
+                            sessionData.setValue('loginInfo', res);
                             initBaseData(() => {
                                 loading.value = false;
                                 resolve(res);

+ 30 - 0
src/business/table/index.ts

@@ -0,0 +1,30 @@
+import { shallowRef } from 'vue'
+import { sessionData } from '@/stores'
+
+export function useTableColumns(tableKey: string) {
+    const tableColumns = sessionData.getValue('tableColumns')
+
+    const columns = shallowRef(tableColumns.reduce((res, cur) => {
+        if (cur.tablekey === tableKey) {
+            cur.columns.forEach((e) => {
+                res.push({
+                    prop: e.columnfield,
+                    label: e.columntitle,
+                    width: Number(e.columnwidth),
+                    show: e.isshow ? true : false,
+                    fixed: e.fixed,
+                })
+            })
+        }
+        return res
+    }, [] as Ermcp.TableColumn[]))
+
+    const updateColumns = () => {
+        console.log('更新表头列')
+    }
+
+    return {
+        columns,
+        updateColumns,
+    }
+}

+ 12 - 1
src/constants/enum/diamond.ts

@@ -1,7 +1,18 @@
 import { getEnumTypeList, getEnumTypeInfo } from './index'
 
 /**
- * 获取商品分类列表
+ * 钻石类型
+ */
+export enum Category {
+    Diamonds = 1, // 成批裸钻
+    Diamond = 2, // 单颗裸钻
+    Rough = 3, // 成批毛坯
+    Jewelry = 4, // 钻石首饰
+    Fancy = 5, // 彩色钻石
+}
+
+/**
+ * 获取钻石分类列表
  * @returns 
  */
 export function getCategoryList() {

+ 5 - 5
src/constants/enum/index.ts

@@ -1,6 +1,4 @@
-import { storageData } from '@/stores'
-
-const enumList = storageData.getEnumListRef()
+import { sessionData } from '@/stores'
 
 /**
  * 枚举类型
@@ -16,7 +14,8 @@ export interface EnumType {
  * @returns 
  */
 export function getEnumTypeList(code: string) {
-    const result = enumList.value.filter((e) => e.enumdiccode.toLowerCase() === code.toLowerCase() && e.enumitemstatus === 1)
+    const allEnums = sessionData.getValue('allEnums')
+    const result = allEnums.filter((e) => e.enumdiccode.toLowerCase() === code.toLowerCase() && e.enumitemstatus === 1)
     return result.map((e) => ({
         label: e.enumdicname,
         value: e.enumitemname,
@@ -30,7 +29,8 @@ export function getEnumTypeList(code: string) {
  * @returns 
  */
 export function getEnumTypeInfo(code: string, value: number) {
-    return enumList.value.find((e) => e.enumdiccode.toLowerCase() === code.toLowerCase() && e.enumitemstatus === 1 && e.enumitemname === value)
+    const allEnums = sessionData.getValue('allEnums')
+    return allEnums.find((e) => e.enumdiccode.toLowerCase() === code.toLowerCase() && e.enumitemstatus === 1 && e.enumitemname === value)
 }
 
 /**

+ 9 - 9
src/hooks/auth/index.ts

@@ -1,13 +1,13 @@
 import { defineAsyncComponent, Component } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import { AuthType } from '@/constants/enum/menu'
 import { AuthMenu } from './interface'
 
 export function useAuth(code?: string) {
     const route = useRoute()
     const router = useRouter()
-    const accountMenus = storageData.getAccountMenus()
+    const userMenus = sessionData.getValue('userMenus')
     const componentMap = new Map<string, Component>()
 
     /**
@@ -17,14 +17,14 @@ export function useAuth(code?: string) {
      */
     const getMenus = (level = 0) => {
         // 过滤层级
-        const filterLevel = (list: Ermcp.AccountMenu[], n: number): Ermcp.AccountMenu[] => {
+        const filterLevel = (list: Ermcp.UserMenu[], n: number): Ermcp.UserMenu[] => {
             if (level) {
                 return list.map((e) => ({ ...e, children: n <= 1 ? [] : filterLevel(e.children ?? [], n - 1) }))
             }
             return list
         }
         // 过滤菜单
-        const filterMenu = (list: Ermcp.AccountMenu[], parentPath = '') => {
+        const filterMenu = (list: Ermcp.UserMenu[], parentPath = '') => {
             const result: AuthMenu[] = []
             list.forEach((e) => {
                 if (e.authType === AuthType.Menu) {
@@ -40,7 +40,7 @@ export function useAuth(code?: string) {
             })
             return result
         }
-        return filterMenu(filterLevel(accountMenus, level))
+        return filterMenu(filterLevel(userMenus, level))
     }
 
     /**
@@ -71,7 +71,7 @@ export function useAuth(code?: string) {
      */
     const getChildrenAuth = () => {
         const routeName = code ?? route.name?.toString()
-        const filter = (list: Ermcp.AccountMenu[]): Ermcp.AccountMenu | undefined => {
+        const filter = (list: Ermcp.UserMenu[]): Ermcp.UserMenu | undefined => {
             if (routeName) {
                 for (const item of list) {
                     const { code, children } = item;
@@ -84,7 +84,7 @@ export function useAuth(code?: string) {
             }
             return undefined;
         }
-        return filter(accountMenus);
+        return filter(userMenus);
     }
 
     /**
@@ -107,7 +107,7 @@ export function useAuth(code?: string) {
                     res.push(cur);
                 }
                 return res;
-            }, [] as Ermcp.AccountMenu[])
+            }, [] as Ermcp.UserMenu[])
         }
         return [];
     }
@@ -151,7 +151,7 @@ export function useAuth(code?: string) {
     return {
         route,
         router,
-        accountMenus,
+        userMenus,
         componentMap,
         getMenus,
         getChildrenMenus,

+ 2 - 2
src/hooks/echarts/candlestick/options.ts

@@ -1,11 +1,11 @@
 import { reactive, watch } from 'vue'
 import { ECOption } from '@/components/base/echarts/core'
 import { timerInterceptor } from '@/utils/timer'
-import { storageData } from '@/stores'
+import { localData } from '@/stores'
 import { EchartsDataset, EchartsOptions, Colors } from './interface'
 import moment from 'moment'
 
-const theme = storageData.getThemeRef();
+const theme = localData.getRef('appTheme');
 
 function getColors() {
     // 默认主题色配置

+ 2 - 2
src/hooks/echarts/timeline/options.ts

@@ -1,11 +1,11 @@
 import { reactive, watch } from 'vue'
 import { timerInterceptor } from '@/utils/timer'
-import { storageData } from '@/stores'
+import { localData } from '@/stores'
 import { echarts } from '@/components/base/echarts/core'
 import { EchartsDataset, EchartsOptions, Colors } from './interface'
 
 
-const theme = storageData.getThemeRef();
+const theme = localData.getRef('appTheme');
 
 function getColors() {
     // 默认主题色配置

+ 7 - 7
src/mock/router.ts

@@ -20,8 +20,8 @@ const appmenu = {
                 authType: 1,
                 sort: 2,
                 title: '钻石搜索',
-                code: 'diamond',
-                url: '/diamond',
+                code: 'search',
+                url: '/search',
                 urlType: 1,
                 component: 'Main',
                 icon: 'TrendCharts',
@@ -30,19 +30,19 @@ const appmenu = {
                         authType: 1,
                         sort: 1,
                         title: '成批裸钻',
-                        code: 'diamond_multiple',
+                        code: 'search_diamonds',
                         url: '',
                         urlType: 1,
-                        component: 'views/diamond/multiple/index.vue',
+                        component: 'views/search/diamonds/index.vue',
                     },
                     {
                         authType: 1,
                         sort: 2,
                         title: '单颗裸钻',
-                        code: 'diamond_single',
-                        url: 'single',
+                        code: 'search_diamond',
+                        url: 'diamond',
                         urlType: 1,
-                        component: 'views/diamond/single/index.vue'
+                        component: 'views/search/diamond/index.vue'
                     },
                 ]
             },

+ 3 - 3
src/packages/mobile/router/index.ts

@@ -1,5 +1,5 @@
 import { createWebHashHistory, RouteRecordRaw } from 'vue-router'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import service from '@/services'
 import Page from '@mobile/components/layouts/page/index.vue'
 import animateRouter from './animateRouter'
@@ -65,11 +65,11 @@ const router = animateRouter.create({
 
 // 路由跳转拦截
 router.beforeEach((to, from, next) => {
-  const isLogin = storageData.getLoginInfo('Token');
+  const token = sessionData.getLoginInfo('Token');
 
   // 判断服务是否加载完成
   if (service.isReady) {
-    if (to.meta.requireAuth && !isLogin) {
+    if (to.meta.requireAuth && !token) {
       next({
         name: 'login',
         query: { redirect: to.fullPath },

+ 4 - 4
src/packages/mobile/views/home/components/market/index.vue

@@ -34,7 +34,7 @@
 <script lang="ts" setup>
 import { ref, reactive, onActivated, onDeactivated } from 'vue'
 import { Grid, GridItem, Button, ActionSheet, ActionSheetAction } from 'vant'
-import { futuresStore, storageData } from '@/stores'
+import { futuresStore, localData } from '@/stores'
 import { handlePriceColor, handleNoneValue } from '@/filters'
 import AppTable from '@mobile/components/base/table/index.vue'
 import { TableColumn } from '@mobile/components/base/table/interface'
@@ -62,15 +62,15 @@ const actions: ActionSheetAction[] = [
 const themeChange = (action: ActionSheetAction) => {
   switch (action.name) {
     case '默认': {
-      storageData.setTheme('Default');
+      localData.setTheme('Default');
       break;
     }
     case '浅色': {
-      storageData.setTheme('Light');
+      localData.setTheme('Light');
       break;
     }
     case '深色': {
-      storageData.setTheme('Dark')
+      localData.setTheme('Dark')
       break;
     }
   }

+ 2 - 2
src/packages/pc/App.vue

@@ -14,7 +14,7 @@ export default {
 <script lang="ts" setup>
 import { ref, watch } from 'vue'
 import { useRoute } from 'vue-router'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import zhCn from 'element-plus/lib/locale/lang/zh-cn'
 
 const route = useRoute()
@@ -22,7 +22,7 @@ const isReady = ref(false)
 
 watch(() => route.name, (routeName) => {
   if (!isReady.value) {
-    const token = storageData.getLoginInfo('Token')
+    const token = sessionData.getLoginInfo('Token')
     if (routeName !== 'boot' && token) {
       isReady.value = true
     }

+ 4 - 4
src/packages/pc/components/layouts/sidebar/index.less

@@ -62,6 +62,8 @@
     }
 
     &__menu {
+        --el-menu-item-height: 44px;
+
         .el-menu {
             border          : 0;
             background-color: transparent;
@@ -86,10 +88,8 @@
             }
 
             .el-menu-item {
-                height       : 44px;
-                //line-height: 44px; bug 会导致鼠标经过严重卡顿
-                color        : var(--sidebar-menu-item);
-                min-width    : auto;
+                color    : var(--sidebar-menu-item);
+                min-width: auto;
 
                 &:hover {
                     color           : #fff;

+ 4 - 4
src/packages/pc/components/layouts/sidebar/index.vue

@@ -4,7 +4,7 @@
       <span class="logo">{{ $t('app.name') }}</span>
     </div>
     <div class="app-sidebar__menu">
-      <app-sidemenu :collapse="collapse" @select="routerTo" />
+      <app-sidemenu :collapse="collapse" @click="routerTo" />
     </div>
     <div :class="['app-sidebar__copyright', collapse && 'is-hide']">
       <span>&copy;{{ year }} Muchinfo</span>
@@ -35,16 +35,16 @@ const hideSidebar = () => {
 }
 
 // 路由跳转
-const routerTo = (routeName: string) => {
+const routerTo = (active: string) => {
   if (state.isMobile) {
     hideSidebar()
   }
-  const submenus = getChildrenMenus(routeName)
+  const submenus = getChildrenMenus(active)
   // 判断是否存在子菜单
   if (submenus.length) {
     router.push({ name: submenus[0].name })
   } else {
-    router.push({ name: routeName })
+    router.push({ name: active })
   }
 }
 

+ 15 - 8
src/packages/pc/components/layouts/sidemenu/index.vue

@@ -1,8 +1,8 @@
 <template>
     <el-menu class="app-sidemenu" :default-active="activeMenu" unique-opened>
-        <AppSubmenu :data-list="menus" />
+        <AppSubmenu :data-list="menus" @click="onMenuClick" />
         <!--系统管理,生产环境需隐藏-->
-        <!-- <el-menu-item index="system_menu">
+        <!-- <el-menu-item index="system" @click="routerTo">
             <app-icon class="menu-icon" icon="Setting" />
             <span>系统管理</span>
         </el-menu-item> -->
@@ -15,16 +15,23 @@ import { useAuth } from '@/hooks/auth'
 import AppIcon from '@pc/components/base/icon/index.vue'
 import AppSubmenu from './submenu.vue'
 
-const { route, getMenus } = useAuth()
+const emit = defineEmits(['click'])
+const { route, router, getMenus } = useAuth()
 const level = 1 // 菜单层级
 const menus = getMenus(level) // 如果是无限级菜单,activeMenu 应该直接返回 route.name
 
 // 高亮菜单
 const activeMenu = computed(() => {
-    const flag = menus.some((e) => route.matched.some((m) => m.name === e.name))
-    if (flag) {
-        return route.matched[level - 1].name
-    }
-    return ''
+    return route.matched[level - 1]?.name ?? route.name
 })
+
+// 点击菜单
+const onMenuClick = (active: string) => {
+    emit('click', active)
+}
+
+// 路由跳转
+const routerTo = () => {
+    router.push({ name: 'system_menu' })
+}
 </script>

+ 13 - 1
src/packages/pc/components/layouts/sidemenu/submenu.vue

@@ -15,7 +15,7 @@
             <!--如果存在子级,递归组件自身-->
             <app-submenu :data-list="item.children" />
         </el-sub-menu>
-        <el-menu-item :index="item.name" v-else>
+        <el-menu-item :index="item.name" @click="onMenuClick" v-else>
             <app-icon class="menu-icon" :icon="item.icon" v-if="item.icon" />
             <span>{{ item.label }}</span>
         </el-menu-item>
@@ -24,6 +24,7 @@
 
 <script lang="ts">
 import { defineComponent, PropType } from 'vue'
+import type { MenuItemRegistered } from 'element-plus'
 import { AuthMenu } from '@/hooks/auth/interface'
 import AppIcon from '@pc/components/base/icon/index.vue'
 
@@ -32,11 +33,22 @@ export default defineComponent({
     components: {
         AppIcon
     },
+    emits: ['click'],
     props: {
         dataList: {
             type: Array as PropType<AuthMenu[]>,
             required: true,
         },
     },
+    setup(props, { emit }) {
+        // 点击菜单
+        const onMenuClick = (item: MenuItemRegistered) => {
+            emit('click', item.index)
+        }
+
+        return {
+            onMenuClick
+        }
+    }
 })
 </script>

+ 1 - 1
src/packages/pc/components/modules/auth-component/index.vue

@@ -43,7 +43,7 @@ const props = defineProps({
 const { componentMap, getAuthComponent } = useAuth(props.code)
 const { onChange } = useAttrs()
 const componentId = shallowRef<string>()
-const dataList = shallowRef<Ermcp.AccountMenu[]>([]) // 数据列表
+const dataList = shallowRef<Ermcp.UserMenu[]>([]) // 数据列表
 
 const onTabChange = (code: string) => {
     if (componentId.value !== code) {

+ 1 - 1
src/packages/pc/components/modules/auth-operation/index.vue

@@ -82,7 +82,7 @@ const props = defineProps({
 const { componentMap, getAuthButton } = useAuth(props.code);
 const componentId = shallowRef<string>();
 const contextmenuOption = shallowRef(props.contextmenu);
-const auth = shallowRef<Ermcp.AccountMenu[]>([]);
+const auth = shallowRef<Ermcp.UserMenu[]>([]);
 
 // 数据列表
 const dataList = computed(() => {

+ 6 - 22
src/packages/pc/router/dynamicRouter.ts

@@ -1,7 +1,6 @@
 import { RouteRecordRaw } from 'vue-router'
 import { AuthType } from '@/constants/enum/menu'
-import { queryAccountMenu } from '@/services/api/account'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import router from '../router'
 
 export default new (class {
@@ -24,7 +23,7 @@ export default new (class {
      * @param routes 
      * @param parentName 
      */
-    private addRoutes(routes: Ermcp.AccountMenu[], parentName = '') {
+    private addRoutes(routes: Ermcp.UserMenu[], parentName = '') {
         routes.forEach((item) => {
             if (item.authType === AuthType.Menu && item.component) {
                 let component;
@@ -69,24 +68,9 @@ export default new (class {
      * @returns 
      */
     registerRoutes() {
-        const menus = storageData.getAccountMenus();
-        return new Promise((resolve, reject) => {
-            this.addNotFound();
-            if (menus.length) {
-                this.addRoutes(menus);
-                resolve(true);
-            } else {
-                queryAccountMenu({
-                    success: (res) => {
-                        storageData.setAccountMenus(res.data);
-                        this.addRoutes(res.data);
-                        resolve(true);
-                    },
-                    fail: (err) => {
-                        reject(err);
-                    }
-                })
-            }
-        })
+        const menus = sessionData.getValue('userMenus');
+        this.addNotFound();
+        this.addRoutes(menus);
+        this.isReady = true;
     }
 })

+ 5 - 9
src/packages/pc/router/index.ts

@@ -1,12 +1,12 @@
 import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import dynamicRouter from './dynamicRouter'
 import service from '@/services'
 
 const routes: Array<RouteRecordRaw> = [
     {
         path: '/',
-        redirect: () => storageData.getLoginInfo('Token') ? '/home' : '/login', // 重定向到默认页面
+        redirect: () => sessionData.getLoginInfo('Token') ? '/home' : '/login', // 重定向到默认页面
     },
     {
         path: '/login',
@@ -57,7 +57,7 @@ router.beforeEach((to, from, next) => {
 
     // 判断服务是否加载完成
     if (service.isReady) {
-        if (storageData.getLoginInfo('Token')) {
+        if (sessionData.getLoginInfo('Token')) {
             if (dynamicRouter.isReady) {
                 if (isLoginOrRegister) {
                     next('/');
@@ -66,12 +66,8 @@ router.beforeEach((to, from, next) => {
                 }
             } else {
                 // 注册动态路由
-                dynamicRouter.registerRoutes().then(() => {
-                    dynamicRouter.isReady = true;
-                    next({ ...to, replace: true });
-                }).catch(() => {
-                    // 404?
-                })
+                dynamicRouter.registerRoutes();
+                next({ ...to, replace: true });
             }
         } else {
             dynamicRouter.isReady = false;

+ 21 - 1
src/packages/pc/views/mine/account/index.vue

@@ -1,6 +1,26 @@
 <template>
-    <app-view>资金</app-view>
+    <app-view>
+        <template #header>
+            <app-filter v-bind="{ selectList, buttonList }" />
+        </template>
+        <app-table :data="dataList" v-model:columns="columns" :loading="loading">
+            <template #header>
+                <el-button type="primary">入金申请</el-button>
+                <el-button type="primary">出金申请</el-button>
+            </template>
+        </app-table>
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { ElMessage } from 'element-plus'
+import { useAccountInOut } from '@/business/account'
+import AppTable from '@pc/components/base/table/index.vue'
+import AppFilter from '@pc/components/base/table-filter/index.vue'
+
+const { dataList, columns, selectList, buttonList, loading, getAccountInOutApply } = useAccountInOut()
+
+getAccountInOutApply().catch((err) => {
+    ElMessage.error(err)
+})
 </script>

+ 25 - 1
src/packages/pc/views/mine/info/index.vue

@@ -1,6 +1,30 @@
 <template>
-    <app-view>信息</app-view>
+    <app-view>
+        <el-descriptions title="会员信息" :column="2" style="padding:20px" v-if="userInfo">
+            <el-descriptions-item label="登录代码">{{ userInfo.postalcode }}</el-descriptions-item>
+            <el-descriptions-item label="企业名称">{{ userInfo.customername }}</el-descriptions-item>
+            <el-descriptions-item label="登录账号">{{ userInfo.customername }}</el-descriptions-item>
+            <el-descriptions-item label="手机号">{{ userInfo.mobile2 }}</el-descriptions-item>
+            <el-descriptions-item label="机构代码"></el-descriptions-item>
+            <el-descriptions-item label="法人姓名"></el-descriptions-item>
+            <el-descriptions-item label="证件类型"></el-descriptions-item>
+            <el-descriptions-item label="企业地址"></el-descriptions-item>
+            <el-descriptions-item label="证件号码"></el-descriptions-item>
+            <el-descriptions-item label="联系人"></el-descriptions-item>
+            <el-descriptions-item label="联系电话"></el-descriptions-item>
+        </el-descriptions>
+        <el-divider />
+        <el-descriptions title="签约信息" :column="2" style="padding:20px" v-if="accountInfo">
+            <el-descriptions-item label="资金账号">{{ accountInfo.accountid }}</el-descriptions-item>
+            <el-descriptions-item label="余额">{{ accountInfo.balance }}</el-descriptions-item>
+            <el-descriptions-item label="冻结资金">{{ accountInfo.freezemargin }}</el-descriptions-item>
+            <el-descriptions-item label="可用资金">{{ accountInfo.usedmargin }}</el-descriptions-item>
+        </el-descriptions>
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { useAccount } from '@/business/account'
+
+const { accountInfo, userInfo } = useAccount()
 </script>

+ 0 - 0
src/packages/pc/views/diamond/single/index.vue → src/packages/pc/views/search/diamond/index.vue


+ 0 - 0
src/packages/pc/views/diamond/multiple/index.vue → src/packages/pc/views/search/diamonds/index.vue


+ 3 - 0
src/packages/pc/views/system/menu/components/edit/index.vue

@@ -19,6 +19,9 @@
                 <el-form-item label="权限代码" prop="resourcecode">
                     <el-input v-model="formItem.resourcecode" />
                 </el-form-item>
+                <el-form-item label="按钮代码" prop="buttonname" v-if="formItem.authtype === AuthType.Button">
+                    <el-input v-model="formItem.buttonname" />
+                </el-form-item>
                 <el-form-item label="按钮样式" v-if="formItem.authtype === AuthType.Button">
                     <el-select v-model="formItem.buttontype" style="margin-right:12px">
                         <el-option label="默认" value="default" />

+ 1 - 1
src/packages/pc/views/system/menu/index.vue

@@ -1,6 +1,6 @@
 <template>
     <app-view class="system-menu">
-        <app-table ref="tableRef" :data="dataList" v-model:columns="columns" row-key="code">
+        <app-table ref="tableRef" :data="dataList" v-model:columns="columns" row-key="resourcecode">
             <template #header>
                 <el-button type="primary" icon="Plus" @click="openEditComponent">新增</el-button>
                 <el-button type="primary" :icon="isRowExpansion ? 'FolderOpened' : 'Folder'" @click="tableExpandAll">

+ 3 - 3
src/packages/pc/views/system/role/components/auth/index.vue

@@ -6,7 +6,7 @@
             <el-breadcrumb-item>{{ selectedRow.roleName }}</el-breadcrumb-item>
         </el-breadcrumb>
         <el-scrollbar height="300px">
-            <el-tree :data="accountMenus" :props="{ label: 'title' }" :expand-on-click-node="false" show-checkbox
+            <el-tree :data="userMenus" :props="{ label: 'title' }" :expand-on-click-node="false" show-checkbox
                 check-on-click-node default-expand-all />
         </el-scrollbar>
         <template #footer>
@@ -23,12 +23,12 @@ import AppDrawer from '@pc/components/base/drawer/index.vue'
 
 defineProps({
     selectedRow: {
-        type: Object as PropType<Ermcp.AccountRole>,
+        type: Object as PropType<Ermcp.UserRole>,
         default: () => ({})
     }
 })
 
-const { accountMenus } = useAuth()
+const { userMenus } = useAuth()
 const show = ref(true)
 </script>
 

+ 1 - 1
src/packages/pc/views/system/role/index.vue

@@ -19,7 +19,7 @@ import { queryAccountRole } from '@/services/api/account'
 import AppAuthOperation from '@pc/components/modules/auth-operation/index.vue'
 import AppTable from '@pc/components/base/table/index.vue'
 
-const { dataList } = useDataTable<Ermcp.AccountRole>()
+const { dataList } = useDataTable<Ermcp.UserRole>()
 
 const columns = ref([
     {

+ 6 - 0
src/packages/pc/views/warehousing/stock/components/edit/diamond.vue

@@ -0,0 +1,6 @@
+<template>
+    <div>单颗</div>
+</template>
+
+<script lang="ts" setup>
+</script>

+ 214 - 0
src/packages/pc/views/warehousing/stock/components/edit/diamonds.vue

@@ -0,0 +1,214 @@
+<template>
+    <el-form ref="formRef" class="el-form--horizontal" label-width="100px" label-position="left" :model="formItem"
+        :rules="formRules">
+        <el-form-item label="商品编号" prop="GoodsNo">
+            <el-input placeholder="请输入" v-model="formItem.GoodsNo" />
+        </el-form-item>
+        <el-form-item label="货币类型" prop="ZSCurrencyType">
+            <el-radio-group v-model="formItem.ZSCurrencyType">
+                <el-radio :label="item.value" v-for="(item, index) in enums.currencyTypeList" :key="index">
+                    {{ item.label }}
+                </el-radio>
+            </el-radio-group>
+        </el-form-item>
+        <el-form-item label="总价" prop="Price">
+            <el-input type="number" placeholder="请输入" v-model.number="formItem.Price">
+                <template #append>{{ currencyInfo?.enumitemvalue }}</template>
+            </el-input>
+        </el-form-item>
+        <el-form-item label="总重量" prop="Weight">
+            <el-input type="number" placeholder="请输入" v-model.number="formItem.Weight">
+                <template #append>(克拉)</template>
+            </el-input>
+        </el-form-item>
+        <el-form-item label="平均重量" prop="WeightAvg">
+            <el-input type="number" placeholder="请输入" v-model.number="formItem.WeightAvg">
+                <template #append>(克拉)</template>
+            </el-input>
+        </el-form-item>
+        <el-form-item label="克拉单位">
+            <span>{{ currencyInfo?.param2 }}{{ caratUnit }} (总价/总重量)</span>
+        </el-form-item>
+        <el-form-item class="el-form-item--row" label="形状" prop="ZSSharpType">
+            <el-checkbox-group v-model="formItem.ZSSharpType">
+                <el-checkbox :label="item.value" v-for="(item, index) in enums.shapeTypeList" :key="index">
+                    {{ item.label }}
+                </el-checkbox>
+            </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="颜色" prop="ZSColorType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSColorType1">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.colorTypeList"
+                        :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSColorType2">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.colorTypeList"
+                        :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="切工" prop="ZSCutType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSCutType1">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.cutTypeList"
+                        :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSCutType2">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.cutTypeList"
+                        :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="净度" prop="ZSClarityType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSClarityType1">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.clarityTypeList"
+                        :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSClarityType2">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.clarityTypeList"
+                        :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="抛光" prop="ZSPolishType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSPolishType1">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.polishTypeList"
+                        :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSPolishType2">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.polishTypeList"
+                        :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="对称" prop="ZSSymmetryType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSSymmetryType1">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.symmetryTypeList"
+                        :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSSymmetryType2">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.symmetryTypeList"
+                        :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="荧光" prop="ZSFluorescenceType">
+            <div class="el-form-item--col">
+                <el-select v-model="formItem.ZSFluorescenceType1">
+                    <el-option :label="item.label" :value="item.value"
+                        v-for="(item, index) in enums.fluorescenceTypeList" :key="index" />
+                </el-select>
+                <span>-</span>
+                <el-select v-model="formItem.ZSFluorescenceType2">
+                    <el-option :label="item.label" :value="item.value"
+                        v-for="(item, index) in enums.fluorescenceTypeList" :key="index" />
+                </el-select>
+            </div>
+        </el-form-item>
+        <el-form-item label="仓库" prop="WarehouseID">
+            <el-select v-model="formItem.WarehouseID">
+                <el-option :label="item.warehousename" :value="item.autoid" v-for="(item, index) in warehouseList"
+                    :key="index" />
+            </el-select>
+        </el-form-item>
+        <el-form-item label="市场价" prop="MarketPrice">
+            <el-input type="number" placeholder="请输入" v-model.number="formItem.MarketPrice">
+                <template #append>{{ currencyInfo?.enumitemvalue }}</template>
+            </el-input>
+        </el-form-item>
+        <el-form-item label="商品照片">
+            <app-upload />
+        </el-form-item>
+        <el-form-item class="el-form-item--row" label="备注" prop="Remark">
+            <el-input type="textarea" v-model="formItem.Remark" />
+        </el-form-item>
+        <el-form-item class="el-form-item--row">
+            <el-button :loading="loading" type="primary" @click="onSubmit(1)">添加商品</el-button>
+            <el-button :loading="loading" type="primary">添加并上传</el-button>
+            <el-button :loading="loading" @click="onCancel(false)" plain>取消</el-button>
+        </el-form-item>
+    </el-form>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage } from 'element-plus'
+import type { FormInstance, FormRules } from 'element-plus'
+import { Category } from '@/constants/enum/diamond'
+import { useGoodsForm } from '@/business/goods'
+import AppUpload from '@/components/base/upload/index.vue'
+
+defineProps({
+    selectedRow: {
+        type: Object as PropType<Ermcp.UserRole>,
+        default: () => ({})
+    }
+})
+
+const emit = defineEmits(['cancel'])
+const { loading, formItem, caratUnit, enums, warehouseList, currencyInfo, addGoods } = useGoodsForm(Category.Diamonds)
+const formRef = ref<FormInstance>()
+
+const formRules: FormRules = {
+    ZSCategory: [{ required: true, message: '请选择商品分类' }],
+    GoodsNo: [{ required: true, message: '请输入商品编号', trigger: 'blur' }],
+    ZSCurrencyType: [{ required: true, message: '请选择货币类型' }],
+    Price: [{ required: true, type: 'number', message: '请输入总价' }],
+    Weight: [{ required: true, type: 'number', message: '请输入总重量' }],
+    WeightAvg: [{ required: true, type: 'number', message: '请输入平均重量' }],
+    ZSSharpType: [{ required: true, message: '请选择形状' }],
+    WarehouseID: [{ required: true, message: '请选择仓库' }],
+    ZSColorType: [
+        {
+            required: true,
+            validator: (rule, value, callback) => {
+                const { ZSColorType1, ZSColorType2 } = formItem
+                if (ZSColorType1 && ZSColorType2) {
+                    callback()
+                } else {
+                    callback(new Error('请选择颜色'))
+                }
+            }
+        }
+    ],
+    ZSClarityType: [
+        {
+            required: true,
+            validator: (rule, value, callback) => {
+                const { ZSClarityType1, ZSClarityType2 } = formItem
+                if (ZSClarityType1 && ZSClarityType2) {
+                    callback()
+                } else {
+                    callback(new Error('请选择净度'))
+                }
+            }
+        }
+    ],
+}
+
+const onCancel = (isRefresh = false) => {
+    emit('cancel', isRefresh)
+}
+
+const onSubmit = (addType: number) => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            addGoods(addType).then(() => {
+                ElMessage.success('提交成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('提交失败:' + err)
+            })
+        }
+    })
+}
+</script>

+ 6 - 0
src/packages/pc/views/warehousing/stock/components/edit/fancy.vue

@@ -0,0 +1,6 @@
+<template>
+    <div>彩钻</div>
+</template>
+
+<script lang="ts" setup>
+</script>

+ 24 - 201
src/packages/pc/views/warehousing/stock/components/edit/index.vue

@@ -1,160 +1,20 @@
 <!-- 账号权限 -->
 <template>
     <app-drawer title="商品上架" :width="960" v-model:show="show" :refresh="refresh">
-        <el-form ref="formRef" class="el-form--horizontal" label-width="100px" label-position="left" :model="formItem"
-            :rules="formRules">
-            <el-form-item label="商品分类" prop="ZSCategory">
-                <el-select v-model="formItem.ZSCategory">
-                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.categoryList"
-                        :key="index" />
-                </el-select>
-            </el-form-item>
-            <el-form-item label="商品编号" prop="GoodsNo">
-                <el-input placeholder="请输入" v-model="formItem.GoodsNo" />
-            </el-form-item>
-            <el-form-item label="货币类型" prop="ZSCurrencyType">
-                <el-radio-group v-model="formItem.ZSCurrencyType">
-                    <el-radio :label="item.value" v-for="(item, index) in enums.currencyTypeList" :key="index">
-                        {{ item.label }}
-                    </el-radio>
-                </el-radio-group>
-            </el-form-item>
-            <el-form-item label="总价" prop="Price">
-                <el-input type="number" placeholder="请输入" v-model.number="formItem.Price">
-                    <template #append>{{ currencyInfo?.enumitemvalue }}</template>
-                </el-input>
-            </el-form-item>
-            <el-form-item label="总重量" prop="Weight">
-                <el-input type="number" placeholder="请输入" v-model.number="formItem.Weight">
-                    <template #append>(克拉)</template>
-                </el-input>
-            </el-form-item>
-            <el-form-item label="平均重量" prop="WeightAvg">
-                <el-input type="number" placeholder="请输入" v-model.number="formItem.WeightAvg">
-                    <template #append>(克拉)</template>
-                </el-input>
-            </el-form-item>
-            <el-form-item label="克拉单位">
-                <span>{{ currencyInfo?.param2 }}{{ caratUnit }} (总价/总重量)</span>
-            </el-form-item>
-            <el-form-item class="el-form-item--row" label="形状" prop="ZSSharpType">
-                <el-checkbox-group v-model="formItem.ZSSharpType">
-                    <el-checkbox :label="item.value" v-for="(item, index) in enums.shapeTypeList" :key="index">
-                        {{ item.label }}
-                    </el-checkbox>
-                </el-checkbox-group>
-            </el-form-item>
-            <el-form-item label="颜色" prop="ZSColorType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSColorType1">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.colorTypeList"
-                            :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSColorType2">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.colorTypeList"
-                            :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="切工" prop="ZSCutType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSCutType1">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.cutTypeList"
-                            :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSCutType2">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.cutTypeList"
-                            :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="净度" prop="ZSClarityType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSClarityType1">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.clarityTypeList" :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSClarityType2">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.clarityTypeList" :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="抛光" prop="ZSPolishType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSPolishType1">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.polishTypeList"
-                            :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSPolishType2">
-                        <el-option :label="item.label" :value="item.value" v-for="(item, index) in enums.polishTypeList"
-                            :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="对称" prop="ZSSymmetryType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSSymmetryType1">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.symmetryTypeList" :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSSymmetryType2">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.symmetryTypeList" :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="荧光" prop="ZSFluorescenceType">
-                <div class="el-form-item--col">
-                    <el-select v-model="formItem.ZSFluorescenceType1">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.fluorescenceTypeList" :key="index" />
-                    </el-select>
-                    <span>-</span>
-                    <el-select v-model="formItem.ZSFluorescenceType2">
-                        <el-option :label="item.label" :value="item.value"
-                            v-for="(item, index) in enums.fluorescenceTypeList" :key="index" />
-                    </el-select>
-                </div>
-            </el-form-item>
-            <el-form-item label="仓库" prop="WarehouseID">
-                <el-select v-model="formItem.WarehouseID">
-                    <el-option :label="item.warehousename" :value="item.autoid" v-for="(item, index) in warehouseList"
-                        :key="index" />
-                </el-select>
-            </el-form-item>
-            <el-form-item label="市场价" prop="MarketPrice">
-                <el-input type="number" placeholder="请输入" v-model.number="formItem.MarketPrice">
-                    <template #append>{{ currencyInfo?.enumitemvalue }}</template>
-                </el-input>
-            </el-form-item>
-            <el-form-item label="商品照片">
-                <app-upload />
-            </el-form-item>
-            <el-form-item class="el-form-item--row" label="备注" prop="Remark">
-                <el-input type="textarea" v-model="formItem.Remark" />
-            </el-form-item>
-        </el-form>
-        <template #footer>
-            <el-button :loading="loading" type="primary" @click="onSubmit(1)">添加商品</el-button>
-            <el-button :loading="loading" type="primary">添加并上传</el-button>
-            <el-button :loading="loading" @click="onCancel(false)" plain>取消</el-button>
-        </template>
+        <el-tabs v-model="componentId">
+            <template v-for="(item, index) in categoryList" :key="index">
+                <el-tab-pane :label="item.label" :name="Category[item.value]" />
+            </template>
+        </el-tabs>
+        <component :is="componentMap.get(componentId)" v-bind="{ selectedRow }" style="padding-bottom:24px"
+            @cancel="onCancel" v-if="componentId" />
     </app-drawer>
 </template>
 
 <script lang="ts" setup>
-import { ref, PropType } from 'vue'
-import { ElMessage } from 'element-plus'
-import type { FormInstance, FormRules } from 'element-plus'
-import { useGoodsForm } from '@/business/goods'
+import { shallowRef, defineAsyncComponent, PropType } from 'vue'
+import { getCategoryList, Category } from '@/constants/enum/diamond'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
-import AppUpload from '@/components/base/upload/index.vue'
 
 defineProps({
     selectedRow: {
@@ -163,63 +23,26 @@ defineProps({
     }
 })
 
-const { loading, formItem, caratUnit, enums, warehouseList, currencyInfo, addGoods } = useGoodsForm()
-const show = ref(true)
-const refresh = ref(false)
-const formRef = ref<FormInstance>()
+const componentMap = new Map<string, unknown>([
+    ['Diamonds', defineAsyncComponent(() => import('./diamonds.vue'))],
+    ['Diamond', defineAsyncComponent(() => import('./diamond.vue'))],
+    ['Rough', defineAsyncComponent(() => import('./rough.vue'))],
+    ['Jewelry', defineAsyncComponent(() => import('./jewelry.vue'))],
+    ['Fancy', defineAsyncComponent(() => import('./fancy.vue'))],
+])
 
-const formRules: FormRules = {
-    ZSCategory: [{ required: true, message: '请选择商品分类' }],
-    GoodsNo: [{ required: true, message: '请输入商品编号', trigger: 'blur' }],
-    ZSCurrencyType: [{ required: true, message: '请选择货币类型' }],
-    Price: [{ required: true, type: 'number', message: '请输入总价' }],
-    Weight: [{ required: true, type: 'number', message: '请输入总重量' }],
-    WeightAvg: [{ required: true, type: 'number', message: '请输入平均重量' }],
-    ZSSharpType: [{ required: true, message: '请选择形状' }],
-    WarehouseID: [{ required: true, message: '请选择仓库' }],
-    ZSColorType: [
-        {
-            required: true,
-            validator: (rule, value, callback) => {
-                const { ZSColorType1, ZSColorType2 } = formItem
-                if (ZSColorType1 && ZSColorType2) {
-                    callback()
-                } else {
-                    callback(new Error('请选择颜色'))
-                }
-            }
-        }
-    ],
-    ZSClarityType: [
-        {
-            required: true,
-            validator: (rule, value, callback) => {
-                const { ZSClarityType1, ZSClarityType2 } = formItem
-                if (ZSClarityType1 && ZSClarityType2) {
-                    callback()
-                } else {
-                    callback(new Error('请选择净度'))
-                }
-            }
-        }
-    ],
+const show = shallowRef(true)
+const refresh = shallowRef(false)
+const componentId = shallowRef('')
+const categoryList = getCategoryList()
+
+if (categoryList.length) {
+    const value = categoryList[0].value
+    componentId.value = Category[value]
 }
 
 const onCancel = (isRefresh = false) => {
     show.value = false
     refresh.value = isRefresh
 }
-
-const onSubmit = (addType: number) => {
-    formRef.value?.validate((valid) => {
-        if (valid) {
-            addGoods(addType).then(() => {
-                ElMessage.success('提交成功')
-                onCancel(true)
-            }).catch((err) => {
-                ElMessage.error('提交失败:' + err)
-            })
-        }
-    })
-}
 </script>

+ 6 - 0
src/packages/pc/views/warehousing/stock/components/edit/jewelry.vue

@@ -0,0 +1,6 @@
+<template>
+    <div>首饰</div>
+</template>
+
+<script lang="ts" setup>
+</script>

+ 6 - 0
src/packages/pc/views/warehousing/stock/components/edit/rough.vue

@@ -0,0 +1,6 @@
+<template>
+    <div>毛坯</div>
+</template>
+
+<script lang="ts" setup>
+</script>

+ 17 - 3
src/services/api/account/index.ts

@@ -18,9 +18,9 @@ export function tokenCheck(params: TradeRequest<Proto.TokenCheckReq, Proto.Token
 }
 
 /**
- * 查询户菜单
+ * 查询户菜单
  */
-export function queryAccountMenu(params: HttpRequest<{ rsp: Ermcp.AccountMenu[] }>) {
+export function queryAccountMenu(params: HttpRequest<{ rsp: Ermcp.UserMenu[] }>) {
     return httpRequest('/account/menu', 'get', params);
 }
 
@@ -32,6 +32,13 @@ export function queryLoginId(params: HttpRequest<{ req: { username: string }, rs
 }
 
 /**
+ * 查询登录账户配置信息
+ */
+export function queryLoginData(params: HttpRequest<{ req: Ermcp.LoginQueryReq, rsp: Ermcp.LoginQueryRsp }>) {
+    return httpRequest('/User/LoginQuery', 'get', params);
+}
+
+/**
  * 查询资金账户信息
  */
 export function queryTaAccounts(params: HttpRequest<{ req: Ermcp.TaAccountsReq, rsp: Ermcp.TaAccountsRsp[] }>) {
@@ -41,6 +48,13 @@ export function queryTaAccounts(params: HttpRequest<{ req: Ermcp.TaAccountsReq,
 /**
  * 查询账户角色
  */
-export function queryAccountRole(params: HttpRequest<{ rsp: Ermcp.AccountRole[] }>) {
+export function queryAccountRole(params: HttpRequest<{ rsp: Ermcp.UserRole[] }>) {
     return httpRequest('/account/role', 'get', params);
+}
+
+/**
+ * 查询充值提现
+ */
+export function queryAccountInOutApply(params: HttpRequest<{ req: Ermcp.AccountInOutApplyReq, rsp: Ermcp.AccountOutInApplyRsp[] }>) {
+    return httpRequest('/Qhj/QueryAccountInOutApply', 'get', params);
 }

+ 2 - 2
src/services/http/index.ts

@@ -2,7 +2,7 @@ import axios, { AxiosRequestConfig, Method } from 'axios'
 //import qs from 'qs'
 //import cryptojs from 'crypto-js'
 import { addPending, removePending } from './pending'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import { HttpRequest, HttpResponse, ResultCode } from './interface'
 import service from '@/services'
 
@@ -20,7 +20,7 @@ http.interceptors.request.use(
 
         //请求头签名
         const sign = {
-            token: storageData.getLoginInfo('Token'),
+            token: sessionData.getLoginInfo('Token'),
             signsecret: 'qz7qWOMXKTMT5JlDs5w4NTPwWeR3xhF1v6wqbZ9cExmP6cc3spvNAp1wJJ1SqRI5',
             timestamp: new Date().getTime(),
         };

+ 3 - 3
src/services/language/index.ts

@@ -1,13 +1,13 @@
 import axios from 'axios'
 import { createI18n } from 'vue-i18n'
 import { Language } from '@/constants/enum/language'
-import { storageData } from '@/stores'
+import { localData } from '@/stores'
 
 export default new (class {
     i18n = createI18n()
 
     constructor() {
-        const lang = storageData.getLanguage()
+        const lang = localData.getValue('lang')
         this.setLanguage(lang)
     }
 
@@ -25,6 +25,6 @@ export default new (class {
         }
 
         this.i18n.global.locale = lang
-        storageData.setLanguage(lang);
+        localData.setValue('lang', lang);
     }
 })

+ 3 - 4
src/services/socket/quote/index.ts

@@ -4,7 +4,7 @@ import { FunCode } from '@/constants/enum/funcode'
 import { QuoteRequest } from './interface'
 import { subscribeListToByteArrary } from './build/encode'
 import { parseSubscribeRsp } from './build/decode'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import socket from '../index'
 
 /**
@@ -12,9 +12,8 @@ import socket from '../index'
  * @param params 
  */
 function quoteServerMiddleware(params: QuoteRequest): Promise<Proto.GoodsQuoteRsp[]> {
-    const token = storageData.getLoginInfo('Token');
-    const loginId = storageData.getLoginInfo('LoginID');
-    const content = subscribeListToByteArrary(params.data, token, Long.fromNumber(loginId));
+    const { LoginID, Token } = sessionData.getValue('loginInfo');
+    const content = subscribeListToByteArrary(params.data, Token, Long.fromNumber(LoginID));
 
     return new Promise((resolve, reject) => {
         socket.sendQuoteServer({

+ 2 - 2
src/services/socket/trade/index.ts

@@ -1,7 +1,7 @@
 import { v4 } from 'uuid'
 import { Package50 } from '@/utils/websocket/package'
 import { FunCode } from '@/constants/enum/funcode'
-import { storageData } from '@/stores'
+import { sessionData } from '@/stores'
 import { IMessageHead } from './protobuf/proto'
 import { TradeRequest, TradeResponse } from './interface'
 import Protobuf from './protobuf'
@@ -16,7 +16,7 @@ function getProtoHeader(funCode: keyof typeof FunCode, header?: IMessageHead, ma
         FunCode: FunCode[funCode],
         UUID: v4(),
         AccessID: 0,
-        UserID: storageData.getLoginInfo('UserID'),
+        UserID: sessionData.getLoginInfo('UserID'),
         ...(header ?? {})
     }
     if (marketId) {

+ 5 - 3
src/stores/index.ts

@@ -1,4 +1,4 @@
-import storageData from './modules/storage'
+import { localData, sessionData } from './modules/storage'
 import commonStore from './modules/common'
 import accountStore from './modules/account'
 import futuresStore from './modules/futures'
@@ -7,12 +7,14 @@ import futuresStore from './modules/futures'
  * 重置数据
  */
 const resetStore = () => {
-    storageData.reset()
+    localData.clear()
+    sessionData.clear()
     accountStore.reset()
 }
 
 export {
-    storageData,
+    localData,
+    sessionData,
     commonStore,
     accountStore,
     futuresStore,

+ 2 - 2
src/stores/modules/account.ts

@@ -1,6 +1,6 @@
 import { ref, computed } from 'vue'
 import { queryTaAccounts } from '@/services/api/account'
-import storageData from './storage'
+import { sessionData } from './storage'
 
 /**
  * 账号存储类
@@ -26,7 +26,7 @@ export default new (class {
 
         return queryTaAccounts({
             data: {
-                loginID: storageData.getLoginInfo('LoginID')
+                loginID: sessionData.getLoginInfo('LoginID')
             },
             success: (res) => {
                 const dataList = res.data

+ 37 - 0
src/stores/modules/common.ts

@@ -1,8 +1,45 @@
 import { ref } from 'vue'
+import { queryLoginData } from '@/services/api/account'
+import { sessionData } from './storage'
 
 /**
  * 公共存储类
  */
 export default new (class {
     loading = ref(false)
+    loginData = ref<Ermcp.LoginQueryRsp>({
+        arearole: [],
+        externalExchanges: [],
+        goodsgroups: [],
+        markets: [],
+        systemParams: []
+    })
+
+    /**
+     * 获取登录数据
+     * @returns 
+     */
+    getLoginData = () => {
+        this.loading.value = true
+        return queryLoginData({
+            data: {
+                loginID: sessionData.getLoginInfo('LoginID')
+            },
+            success: (res) => {
+                this.loginData.value = res.data
+            },
+            complete: () => {
+                this.loading.value = false
+            }
+        })
+    }
+
+    /**
+     * 获取登录数据
+     * @param key 
+     * @returns 
+     */
+    getLoginDataInfo = <K extends keyof Ermcp.LoginQueryRsp>(key: K) => {
+        return this.loginData.value[key]
+    }
 })

+ 2 - 2
src/stores/modules/futures.ts

@@ -1,6 +1,6 @@
 import { ref, computed } from 'vue'
 import { queryGoodsList } from '@/services/api/goods'
-import storageData from './storage'
+import { sessionData } from './storage'
 
 /**
  * 期货存储类
@@ -17,7 +17,7 @@ export default new (class {
         this.loading.value = true
         return queryGoodsList({
             data: {
-                userid: storageData.getLoginInfo('UserID')
+                userid: sessionData.getLoginInfo('UserID')
             },
             success: (res) => {
                 this.goodsList.value = res.data

+ 95 - 106
src/stores/modules/storage.ts

@@ -1,4 +1,5 @@
-import { queryAllEnums, queryNewFuncmenu } from '@/services/api/common'
+import { queryAllEnums, queryNewFuncmenu, queryTableDefine } from '@/services/api/common'
+import { queryAccountMenu } from '@/services/api/account'
 import { AppTheme } from '@/constants/enum/theme'
 import { Language } from '@/constants/enum/language'
 import WebStorage from '@/utils/storage'
@@ -8,6 +9,7 @@ import plus from '@/utils/h5plus'
  * 初始数据
  */
 const initData: Store.GlobalStorage = {
+    appTheme: AppTheme.Default,
     lang: Language.ZhCN,
     loginInfo: {
         AccountIDs: [],
@@ -20,21 +22,17 @@ const initData: Store.GlobalStorage = {
         MemberUserID: 0,
         Token: '',
     },
-    bankSignDetail: '',
-    cusBank: '',
-    errorInfos: '',
-    enumList: [],
-    errorCodes: [],
-    appTheme: AppTheme.Default,
-    rowNumber: '',
-    menus: [],
+    userMenus: [],
+    allEnums: [],
+    tableColumns: [],
 }
 
-export default new (class {
-    private localData = new WebStorage(localStorage, initData) // 本地存储实例
-    private sessionData = new WebStorage(sessionStorage, initData) // 会话存储实例
-
+/**
+ * 本地存储实例
+ */
+export const localData = new (class extends WebStorage<Store.GlobalStorage>{
     constructor() {
+        super(localStorage, initData)
         document.addEventListener('DOMContentLoaded', this.loadTheme, false)
     }
 
@@ -42,7 +40,7 @@ export default new (class {
      * 加载主题
      */
     private loadTheme = () => {
-        const theme = this.localData.getValue('appTheme')
+        const theme = this.getValue('appTheme')
         this.setStatusBarTheme(theme)
         document.documentElement.setAttribute('theme', theme)
         document.removeEventListener('DOMContentLoaded', this.loadTheme)
@@ -66,35 +64,6 @@ export default new (class {
     }
 
     /**
-     * 获取登录信息
-     * @param key 
-     * @returns 
-     */
-    getLoginInfo = <K extends keyof Proto.LoginRsp>(key: K) => {
-        return this.localData.getValue('loginInfo')[key] || this.sessionData.getValue('loginInfo')[key]
-    }
-
-    setLoginInfo = (value: Proto.LoginRsp) => {
-        this.sessionData.setValue('loginInfo', value)
-    }
-
-    getAccountMenus = () => {
-        return this.sessionData.getValue('menus')
-    }
-
-    setAccountMenus = (value: Ermcp.AccountMenu[]) => {
-        return this.sessionData.setValue('menus', value)
-    }
-
-    /**
-     * 获取当前主题
-     * @returns 
-     */
-    getThemeRef = () => {
-        return this.localData.getRef('appTheme')
-    }
-
-    /**
      * 设置主题
      * @param key 
      */
@@ -102,85 +71,105 @@ export default new (class {
         const theme = AppTheme[key]
         this.setStatusBarTheme(theme)
         document.documentElement.setAttribute('theme', theme)
-        this.localData.setValue('appTheme', theme)
+        this.setValue('appTheme', theme)
     }
+})
 
-    getLanguage = () => {
-        return this.localData.getValue('lang')
-    }
-
-    setLanguage = (lang: Language) => {
-        this.localData.setValue('lang', lang)
+/**
+ * 会话存储实例
+ */
+export const sessionData = new (class extends WebStorage<Store.GlobalStorage>{
+    constructor() {
+        super(sessionStorage, initData)
     }
 
-    getEnumListRef = () => {
-        return this.sessionData.getRef('enumList')
+    /**
+     * 获取登录信息
+     * @param key 
+     * @returns 
+     */
+    getLoginInfo = <K extends keyof Proto.LoginRsp>(key: K) => {
+        return this.getValue('loginInfo')[key]
     }
 
     /**
-     * 初始化数据
+     * 获取所有枚举列表
+     * @returns 
      */
-    init = async () => {
-        const asyncTask = []
-
-        if (!this.sessionData.getValue('enumList').length) {
-            const task = queryAllEnums({
-                success: (res) => {
-                    this.sessionData.setValue('enumList', res.data)
-                }
-            })
-            asyncTask.push(task)
+    getAllEnumList = () => {
+        if (this.getValue('allEnums').length) {
+            return Promise.resolve()
         }
+        return queryAllEnums({
+            success: (res) => {
+                this.setValue('allEnums', res.data)
+            }
+        })
+    }
 
-        if (!this.sessionData.getValue('menus').length) {
-            const task = queryNewFuncmenu({
-                data: {
-                    menutype: '5',
-                },
-                success: (res) => {
-                    // 扁平列表树形化
-                    const arrayToTree = (list: Ermcp.NewFuncmenuRsp[], code: null | string = null) => {
-                        const getChildren = (parent: null | string) => {
-                            const result: Ermcp.AccountMenu[] = []
-                            list.forEach((e) => {
-                                if (e.parentcode === parent) {
-                                    const item = {
-                                        id: 0,
-                                        authType: e.authtype,
-                                        title: e.resourcename,
-                                        code: e.resourcecode,
-                                        urlType: e.urltype,
-                                        url: e.url,
-                                        component: e.component,
-                                        icon: e.iconame,
-                                        buttonName: e.buttonname,
-                                        buttonType: e.buttontype,
-                                        sort: e.sort,
-                                        hidden: Boolean(e.hidden),
-                                        remark: e.remark,
-                                        children: getChildren(e.resourcecode),
-                                    }
-                                    result.push(item)
+    /**
+     * 获取用户菜单列表
+     */
+    getUserMenuList = () => {
+        return queryAccountMenu({
+            success: (res) => {
+                sessionData.setValue('userMenus', res.data)
+            }
+        })
+        return queryNewFuncmenu({
+            data: {
+                menutype: '5',
+            },
+            success: (res) => {
+                // 扁平列表树形化
+                const arrayToTree = (list: Ermcp.NewFuncmenuRsp[], code: null | string = null) => {
+                    const getChildren = (parent: null | string) => {
+                        const result: Ermcp.UserMenu[] = []
+                        list.forEach((e) => {
+                            if (e.parentcode === parent) {
+                                const item = {
+                                    id: 0,
+                                    authType: e.authtype,
+                                    title: e.resourcename,
+                                    code: e.resourcecode,
+                                    urlType: e.urltype,
+                                    url: e.url || '',
+                                    component: e.component,
+                                    icon: e.iconame,
+                                    buttonName: e.buttonname,
+                                    buttonType: e.buttontype,
+                                    sort: e.sort,
+                                    hidden: Boolean(e.hidden),
+                                    remark: e.remark,
+                                    children: getChildren(e.resourcecode),
                                 }
-                            })
-                            return result
-                        }
-                        return getChildren(code)
+                                result.push(item)
+                            }
+                        })
+                        return result
                     }
-                    //this.sessionData.setValue('menus', arrayToTree(res.data))
+                    return getChildren(code)
                 }
-            })
-            asyncTask.push(task)
-        }
-
-        await Promise.all(asyncTask)
+                this.setValue('userMenus', arrayToTree(res.data))
+            }
+        })
     }
 
     /**
-     * 重置数据
+     * 获取表格列列表
+     * @returns 
      */
-    reset = () => {
-        this.localData.clear()
-        this.sessionData.clear()
+    getTableColumnList = () => {
+        if (this.getValue('tableColumns').length) {
+            return Promise.resolve()
+        }
+        return queryTableDefine({
+            data: {
+                tableType: 2
+            },
+            success: (res) => {
+                this.setValue('tableColumns', res.data)
+            }
+        })
     }
 })

+ 321 - 5
src/types/ermcp/account.d.ts

@@ -3,8 +3,267 @@ import { AuthType, UrlType } from '@/constants/enum/menu'
 declare global {
     /** 企业风管 */
     namespace Ermcp {
-        /** 账号菜单 */
-        interface AccountMenu {
+        /** 账户登录后信息查询请求 */
+        interface LoginQueryReq {
+            loginID: number; // 登录账号
+        }
+
+        /** 账户登录后信息查询响应 */
+        interface LoginQueryRsp {
+            arearole: AreaRole[]
+            externalExchanges: ExternalExchange[],
+            goodsgroups: GoodsGroup[],
+            loginAccount?: LoginAccount,
+            markets: Market[],
+            systemParams: SystemParam[],
+            userAccount?: UserAccount,
+            userInfo?: UserInfo,
+            username?: string,
+        }
+
+        /** 所属角色信息 */
+        interface AreaRole {
+            createtime: string;//创建时间
+            creatorid: number;//创建人ID
+            fromuserid: number;//所属机构(组织机构)( OrgType = 1时为自己, = 2\3时取上级机构的所属机构)
+            isreturnscore: number;//是否返还积分 [经纪会员-辽东湾] - number;
+            //:不返还 1:返还
+            modifierid: number;//修改人ID
+            modifytime: string;//修改时间
+            orgtype: number;//组织机构类型 1:机构\分支节点 2:交易员\叶子节点 3:部门\分支节点
+            outuserid: string;//外部账户[场外期权做市商]
+            parentuserid: number;//上级组织机构(组织机构)
+            roletype: number;//角色类型 - 1:交易所 2:运营机构 3:营销中心 4:仓库机构 5:三方服务机构 6:自营会员 7:经纪会员 8:做市会员 9:产业会员 1number;
+            //:金融机构 11:商城店铺 12:子机构 13:报价商 14:积分服务商 15:供货商 16:圈内会员 17:物流机构 18:报价配置机构 19:场外期权做市商 2number;
+            //:组织机构 21:顶级机构 22:业务员 23:跟单员 24:交易员 25:客户 98:代理 99:门店
+            rootuserid: number;//顶级组织机构用户ID(组织机构)
+            shoppassword: string;//商城店铺登陆密码(商城店铺角色使用)
+            sublevelpath: string;//组织机构层级路径(逗号分隔,首尾加逗号)
+            suborgtype: number;//子机构类型(千海金) - number;
+            //:机构 1:代理 2:门店
+            tradeaccountid: number;//默认交易资金账号ID(做市会员被动接单使用、商城店铺默认资金账 号、圈内会员默认资金账号) - 作废, 使用UserReckonAccount
+            transurl: string;//物流地址(物流机构)
+            userid: number;//机构用户ID其他做市关系,都设置为 1
+        }
+
+        /** 外部交易所 */
+        interface ExternalExchange {
+            autoid: number;//AutoID - SEQ_EXTERNALEXCHANGE
+            exchangefullname: string;//外部交易所全称
+            exexchangecode: string;//外部交易所代码
+            exexchangename: string;//外部交易所名称(简称)
+            ismarketprice: number;//是否支持市价 - 0:不支持 1:支持
+            quotegear: number;//行情档位(1-10)
+            showbrokerflag: number;//是否显示买卖经纪盘 - 0:不显示 1:显示
+            updatetime: string;//更新时间
+        }
+
+        /** 商品组 */
+        interface GoodsGroup {
+            agreeunit: number;//合约单位
+            canshort: number;//是否允许做空[通道交易] - 0:不能做空 1:可以做空
+            closepricemode: number;//强平价格方式 - 1:市价 2:最新价 3:涨跌停(未实现)
+            closepriceparam: number;//强平最新价浮动比例 - 方式为2时使用
+            createtime: string;//创建时间
+            creatorid: number;//创建人
+            currencyid: number;//报价货币ID
+            decimalplace: number;//报价小数位
+            exercisetype: number;//行权类型[场外期权]-1:欧式期权 2:美式期权
+            exexchangeid: number;//外部交易所ID[通道交易]
+            goodsgroupid: number;//商品组ID(自增ID)
+            goodsgroupname: string;//商品组名称
+            goodsgroupstatus: number;//商品组状态 - 1:正常 2:注销
+            goodunitid: number;//报价单位ID
+            innerdealmode: number;//内部成交方式[通道交易] - 1:净头寸 2:开平 3:平今
+            isbuylimited: number;//是否限制建仓量 - 0:不限制 1:限制
+            marketid: number;//所属市场ID
+            modifierid: number;//修改人
+            modifytime: string;//修改时间
+            outerdealmode: number;//外部成交方式[通道交易]- 1:净头寸 2:开平 3:平今
+            outergroupcode: string;//外部商品组代码[通道交易]
+            premiumdecimalplace: number;//权利金小数位[场外期权]
+            premiumspreadalgorithm: number;//权利金点差方式[场外期权] 1:比率 2:固定
+            premiumspreadvalue: number;//权利金点差值[场外期权]
+            qtydecimalplace: number;//成交量小数位
+            quotegear: number;//行情档位(1-10)
+            quoteminunit: number;//行情最小变动单位 [整数,报价小数位一起使用]
+            quoterid: number;//报价商ID[场外期权]
+            quotesourcegroupid: number;//所属行情源分组ID[参考行情市场用\通道交易]
+            syncgoodsqty: number;//同步合约数[通道交易-投资管理用] - 0表示不限
+        }
+
+        interface LoginAccount {
+            canoutin: number;//是否可出入金 - 0:不可 1:可
+            clientroleid: number;//终端角色ID
+            haslogined: number;//是否已登录 - 0:未登录 1:已登录
+            hasupdatedpwd: number;//是否已更改密码 - 0:未修改 1:已修改
+            lastloginip: string;//最新登录地址
+            lastloginmode: number;//最新登录方式(客户端类型) - 0:保留为未填终端类型 1:PC管理端 2:PC交易端 3:手机客户端安卓 4:网页客户端 5:微信客户端 6:手机客户端苹果 7:网上开户客户端 8:无效终端编号 9:报价终端(中江)
+            lastloginremark: string;//最新登录描述
+            lastlogintime: string;//最新登录时间
+            logincode: string;//登录代码
+            loginfailnum: number;//连续登录失败次数(登录成功时清零)
+            loginid: number;//登陆账号
+            loginport: number;//最新登录端口
+            loginstatus: number;//登录账号状态 1:正常 2:冻结 3:无效
+            loginusertype: number;//登录账号类型 - 1:投资者 2:机构交易员
+            modifierid: number;//修改人
+            modifytime: string;//修改时间
+            pwdwrongcount: number;//密码错误次数
+            taaccountrighttype: number;//资金账号权限类型 - 1:不选默认为所有权限 2:不选默认为无权限
+            unfreezetime: string;//账户解冻时间
+            userid: number;//用户ID
+        }
+
+        /** 市场 */
+        interface Market {
+            auctionwrtype: number;//竞拍仓单类型 - 1:无仓单 2:有仓单 3;有无仓单均可
+            canacceptquote: number;//确认行权是否接收行情 - 0:不接受 1:接受 [可确认权的挂牌期权市场可配置]
+            cangoodsexercise: number;//[期权]是否可现货行权- 0:否 1:是
+            cangoodsexercisetype: number;//可现货行权期权类型 - 1:认购 2认沽 3:认购认沽 [CanGoodsExercise = 1时可设置]
+            canmanualquotestrike: number;//是否可手动报行权价- 0:否 1:是 [期权]
+            canmutistage: number;//是否可多段运行 – 0:不可 1:可 [挂牌期权]
+            canpreexercise: number;//[期权]是否可预申报- 0:否 1:是
+            clearinterval: number;//待开市间隔(交易市场开盘前多久发市场待开市信号(单位分钟))
+            contracttmp: string;//合同模板[荷兰式][竞价式][仓单贸易]
+            exchareaid: number;//所属交易所,可以没有
+            goodstype: number;//商品类型 - 1:交易商品 2:仓单商品
+            hasrebate: number;//是否返利[竞价式] 0:不返,1:返利 – 根据系统参数088显示或隐藏
+            hastradecredit: number;//是否交易授信[做市收益权] - 0:不授信 1:授信
+            haswr: number;//[竞拍]是否需要仓单 - 0:不需要 1:需要-作废
+            isdeductmargin: number;//竞拍违约是否扣除保证金[竞拍-降价式] - 0:不扣 1:扣除
+            isrecordsource: number;//是否记录成交源 - 0:不记录 1:记录 [所有权]
+            isreleasemargin: number;//成交参与保证金是否释放[竞价式] - 0:不释放 1释放
+            marginformula: number;//持仓保证金公式 - 1:双边收 2:净头寸收 3:大小边差异收 4:大边收 5:卖持仓收(仅期权)
+            marginformula2: number;//持仓保证金公式(仅受托竞价) - 1:双边收 2:净头寸收 3:大小边差异收 4:大边收 5:卖持仓收(仅期权)
+            marketid: number;//市场ID正常5位,前三位固定:两位表示交易模式, 一位表示交易属性(1:收益权,2:所有权) 其它特殊市场:0-系统 1-交割服务 2-账户服务3-履约服务 4-仓单服务 5-积分服务 6-银行服务
+            marketname: string;//市场名称
+            marketserviceid: number;//市场服务ID
+            marketstatus: number;//生效状态(ValidStatus枚举): 1:待生效 2:正常 3:注销
+            markettype: number;//市场类型- 1:非交易服务 2:交易服务
+            matchermode: number;//指定对手模式[仓单贸易模式专用] - 1:任意对手
+            openmethod: number;//开盘模式 - 0 自动 1手动
+            otcuserid: number;//场外期权做市商[场外期权]
+            outersynctime: string;//外部同步时间点(h24:mi:ss)[场外期权]
+            pendingflag: number;//待开市时间标识[通道交易-对冲] - 0:当日 1:上日
+            pendingtime: string;//待开市时间通道交易-对冲
+            performancetempid: number;//履约计划模板ID - 作废
+            premiumquotemode: number;//权利金报价方式 - 1:自动 2:手动
+            rebateratio: number;//返利比率[竞价式]
+            reckonorder: number;//结算顺序
+            reckonpricealgorithm: number;//结算价算法: 1:最后多少笔成交价加权平均 2:最后多少秒成交价加权平均 3:全天加权平均 4:最后一口价 5.买一价 6.卖一价 7.买一卖一均价 8.外部结算价
+            reckonpriceparam: number;//结算价参数
+            reckontime: string;//结算时间通道交易-对冲
+            roleprioritytype: number;//角色优先类型 - 1:无 2:报价商优先 3:非报价商优先 [16:挂牌点选]
+            selllistingauditflag: number;//卖挂牌是否需要审核(仓单贸易) - 0:不需要 1:需要
+            trademarkettype: number;//交易市场类型 - 1:合约市场 2:外部市场 3:仓单市场
+            trademode: number;//交易模式 - 10:做市 13:竞价 15:通道交易 16:挂牌点选 17:仓单贸易 18:期权 19:竞拍-降价式 20:竞拍-竞价式 21:竞拍-大宗式 22:受托竞价
+            tradeproperty: number;//交易属性 - 1:收益权(可做空) 2:所有权(不可做空) 3:期权 4:现货 5:参考行情 6:通道交易 7:币交易 8:场外期权
+            tradetype: number;//下单方式[通道交易] - 1:直接转单 2:净头寸下单
+        }
+
+        /** 系统参数 */
+        interface SystemParam {
+            loginid: string;//更新用户账号,登录账号
+            modifyflag: number;//修改标志0 不允许修改 1 允许修改
+            paramcode: string;//参数代码
+            paramname: string;//参数名
+            paramtype: number;//参数类型 0-通用 1-管理端 2-终端 3-后台 4-商城
+            paramvalue: string;//参数值
+            paramvalue2: string;//参数值(CLOB)
+            regexpress: string;//正则表达式
+            remark: string;//备注
+            serverip: string;//限制服务器IP,备份参数,现在界面不用显示0.0.0.0则无限制,是通用参数192.168.0.211 指定服务器的参数配置
+            showflag: number;//是否显示0 不显示 1 显示
+            updatetime: string;//更新时间
+            userid: number;//更新用户ID
+        }
+
+        interface UserAccount {
+            accountname: string;//账户名称(机构名称)
+            accountstatus: number;//账户状态 - 1:待激活 2:待审核 3:待复审 4:正常 5:审核拒绝 6:注销
+            auditremark: string;//审核备注
+            audittime: string;//审核时间
+            audituserid: number;//审核人
+            broker: number;//所属经纪人ID
+            canceltime: string;//销户时间
+            canceluserid: number;//销户人
+            createtime: string;//创建时间
+            creatorid: number;//创建人
+            hasauth: number;//是否已实名认证 - 0:未认证 1:已认证 2:已提交(待审核) 3:已拒绝
+            isanonymous: number;//是否匿名下单 - 0:否 1:是
+            maxinvestornum: number;//最大用户数(经纪会员下投资者个数)
+            memberuserid: number;//所属会员ID
+            modifierid: number;//修改人
+            modifyremark: string;//变更备注
+            modifystatus: number;//变更状态 1 未变更 2 变更中 3 变更待审核 4 变更待复核(投资者)
+            modifytime: string;//修改时间
+            parentuserid: number;//所属机构ID
+            reckonaccountid: number;//默认结算资金账号ID(机构分润使用) 作废
+            refercount: number;//推荐总人数
+            refereeuserid: number;//推荐人ID
+            refernum: string;//推荐码
+            subarealevelpath: string;//子机构层级路径(逗号分隔,首尾加逗号)
+            userid: number;//用户ID
+            usertype: number;//账户类型 - 1:交易所 2:机构 3:会员子机构 4:经纪人 5:投资者 6:客户
+        }
+
+        interface UserInfo {
+            address: string;//地址
+            attachment1: string;//附件1
+            attachment2: string;//附件2
+            bankaccount: string;//银行帐号 (加密存储)
+            bankaccountname: string;//收款人名称
+            bankcardfrontphotourl: string;//银行卡正面照地址
+            bankid: string;//银行编码
+            bankname: string;//银行名称
+            biznature: number;//企业性质( 企业) - 1:国有控股企业 2:集体控股企业 3:私人控股企业 4:港澳台商控股企业 5:外商控股企业 6:其它
+            bizscope: string;//企业经营范围(企业)
+            cardbackphotourl: string;//证件背面图片地址
+            cardfrontphotourl: string;//证件正面图片地址
+            cardnum: string;//证件号码(加密存储)
+            cardtypeid: number;//证件类型ID
+            cityid: number;//市
+            company: string;//公司(个人)
+            contactname: string;//联系人
+            countryid: number;//国家
+            createtime: string;//创建时间
+            creatorid: number;//创建人
+            customername: string;//客户名称(企业名称)
+            districtid: number;//地区
+            email: string;//邮件(加密存储)
+            fax: string;//传真(加密存储)
+            halfbodyphotourl: string;//半身照地址
+            hasencrypt: number;//数据是否已加密 - 0:未加密 1:已加密
+            headurl: string;//头像地址
+            legalcardbackphotourl: string;//法人身份证背面照地址
+            legalcardfrontphotourl: string;//法人身份证正面照地址
+            legalpersonname: string;//法人姓名(企业)
+            mobile: string;//手机号码(加密存储)
+            mobile2: string;//手机号码[明文-尚志]
+            modifierid: number;//修改人
+            modifiertime: string;//修改时间
+            needinvoice: number;//是否需要发票 - 0:不需要 1:需要
+            nickname: string;//昵称:默认为名称脱敏(张) 或 手机号脱敏(1399999)
+            openmode: number;//开户方式 - 1:管理端开户 2:网上开户注册(会员官网) 3:微信开户 4:网页交易端注册 5:安卓手机端注册 6:苹果手机端注册 7:PC交易端注册 8:微信快速开户 9:支付宝快速开户 10:手机号快速开户
+            otherurl: string;//其它图片地址[使用分号分隔]
+            postalcode: string;//邮政编码
+            provinceid: number;//省
+            qq: string;//QQ(加密存储
+            remark: string;//备注
+            sex: number;//用户性别 0: 女 1: 男
+            signpdfurl: string;//签约pdf文件
+            telphone: string;//联系电话(加密存储)
+            userid: number;//用户ID
+            userinfotype: number;//用户信息类型 - 1:个人 2:企业
+            userstatus: number;//用户状态 - 1:正常 2:注销
+            usertype: number;//账户类型 - 1:交易所 2:机构 3:会员子机构 4:经纪人 5:投资者 6:客户
+            wechat: string;//微信(加密存储)
+            wskhinfo: string;//开户申请信息(JSON)
+        }
+
+        /** 用户菜单 */
+        interface UserMenu {
             id: number; // 自增ID
             authType: AuthType; // 权限类型
             title: string; // 标题
@@ -18,11 +277,11 @@ declare global {
             sort: number; // 排序
             hidden: boolean; // 是否隐藏
             remark: string; // 备注
-            children?: AccountMenu[],
+            children?: UserMenu[],
         }
 
-        /** 账号角色 */
-        interface AccountRole {
+        /** 用户角色 */
+        interface UserRole {
             id: number; // 自增ID
             roleName: string; // 角色名称
             createdAt: string;  // 创建时间
@@ -91,5 +350,62 @@ declare global {
             usedmargin: number; // 占用保证金
             userid: number; // 用户ID
         }
+
+        /** 查询充值提现请求 */
+        interface AccountInOutApplyReq {
+            userid: number;  // 用户ID
+            begindate?: string;  //   申请起始日期(格式yyyymmdd)
+            enddate?: string;    //   申请截止日期(格式yyyymmdd)
+            likename?: string;   //   模糊搜索名称
+        }
+
+        /** 查询充值提现返回 */
+        interface AccountOutInApplyRsp {
+            accountcode: string;//资金账号
+            accountname: string;//用户名称(名称)
+            accountpwd: string;//资金密码
+            accountticket: string;//最新账户服务流水号
+            amount: number;//金额
+            applyremark: string;//申请备注
+            applystatus: number;//申请状态 - 1:待审核 2:待复审 3:初审拒绝 4:交易冻结中 5:交易解冻中 6:交易解冻扣款中 7:交易入金中 8:交易冻结/解冻/扣款中(银行发起出金时用) 9:银行出金中 1number;
+            //:银行入金中 11:成功 12:失败 13:银行审核中 14:账户服务入金失败; 15:账户服务解冻失败; 16:账户服务解冻扣款失败; 17:账户服务出金失败 18:复审通过 19:复审拒绝 2number;
+            //:提交审核,账户冻结中 21:审核拒绝,账户解冻中;22: 待审核,账户服务解冻回滚中; 23:待复审,账户服务解冻回滚中; 24: 审核通过,账户冻结金额检查中;25: 复审通过,账户冻结金额检查中;
+            auditid: number;//审核人
+            audittime: string;//审核时间
+            bank_apply_ticket: string;//银行申请流水
+            bankaccountname: string;//银行账户名
+            bankaccountno: string;//银行卡号
+            bankid: string;//银行编号
+            bankticket: string;//银行流水
+            bankname: string; // 银行名称
+            branchbankid: string;//银行支行号
+            branchbankname: string;//银行支行名称
+            capamountout: number;//出金(劣后本金) - 外部子账户
+            certificatephotourl: string;//凭证地址
+            charge: number;//手续费
+            checkerrorflag: number;//对账差错标志 - 1:为单边账;其它为正常出入金
+            currency: string;//币种
+            cusbankid: string;//托管银行编号
+            exchticket: string;//银行服务流水号
+            executetype: number;//申请类型 - 1:出金 2:入金 3: 单边账调整:入金; 4:单边账调整:出金 5:外部母账户调整:入金 6:外部母账户调整:出金 7:外部子账户:入金 8:外部子账户:出金
+            extendinfo: string;//扩展信息
+            extoperateid: number;//交易服务流水号
+            infamount: number;//劣后金额(自有)
+            logincode: string;//登录账号(账号)
+            netaddr: string;//调转网址
+            priamount: number;//优先金额(授信)
+            reauditid: number;//复审人
+            reauditremark: string;//复审备注
+            reaudittime: string;//复审时间
+            relatedorderid: string;//三方关联ID
+            remark: string;//备注
+            remark2: string;//备注(失败原因)
+            soucreamount: number;//原始出入金金额
+            soucrecurrencyid: number;//原始出入金币种
+            tradedate: string;//交易日(yyyyMMdd)
+            updatetime: string;//更新时间(申请时间)
+            userid: number;//用户id
+            userinfotype: number;//账户类型 1-个人 2-企业
+        }
     }
 }

+ 4 - 20
src/types/store/globalStorage.d.ts

@@ -5,28 +5,12 @@ declare global {
     namespace Store {
         /** 本地缓存数据 */
         interface GlobalStorage {
+            appTheme: AppTheme; // 应用主题色
             lang: Language;
             loginInfo: Proto.LoginRsp; // 账号登录返回信息 token等
-            bankSignDetail: string; // 银行签约信息详情
-            cusBank: string; // 托管银行
-            errorInfos: string; // 数据库错误信息
-            enumList: Ermcp.EnumRsp[]; // 枚举列表
-            errorCodes: GlobalStorage.ErrorCode[];
-            appTheme: AppTheme; // 应用主题色
-            rowNumber: string; // 数据库错误信息 时间戳
-            menus: Ermcp.AccountMenu[];
-        }
-
-        namespace GlobalStorage {
-            /** 错误代码 */
-            interface ErrorCode {
-                description: string;
-                errorcode: string;
-                errorid: number;
-                modulecode: string;
-                operatecode: string;
-                rownumber: string;
-            }
+            userMenus: Ermcp.UserMenu[];
+            allEnums: Ermcp.EnumRsp[]; // 所有枚举
+            tableColumns: Ermcp.TableDefineRsp[];
         }
     }
 }

+ 70 - 34
src/utils/storage/index.ts

@@ -1,65 +1,101 @@
-import { readonly, toRef, ref, Ref } from 'vue'
-import { merge } from '../object'
+import { reactive, toRef, readonly, computed, WritableComputedRef, UnwrapNestedRefs } from 'vue'
 
 /**
- * 存储类
+ * 本地存储类
  */
 export default class <T extends object> {
     private readonly storage: Storage
-    private readonly source // 始数据
+    private readonly source // 始数据
     private state
 
     constructor(storage: Storage, source: T) {
         this.storage = storage
         this.source = source
-        this.state = ref(source)
+        this.state = reactive({ ...source })
+
+        // 读取本地数据
+        for (const key in this.state) {
+            const strValue = this.storage.getItem(key)
+            if (strValue !== 'undefined' && strValue !== null) {
+                this.state[key] = JSON.parse(strValue)
+            }
+        }
     }
 
-    /** 获取存储数据 */
-    private getStorage<K extends keyof T>(key: K) {
-        const str = this.storage.getItem(key.toString())
-        if (str === 'undefined' || str === null) {
-            this.storage.removeItem(key.toString())
-            this.state.value[key] = this.source[key]
-        } else {
-            const value = JSON.parse(str)
-            this.state.value[key] = value
+    /**
+     * 获取状态数据 (不建议使用,可能存在对象内部值变更无法同步 Storage 的问题)
+     * @returns 
+     */
+    getState = () => {
+        const state = {} as { [key in keyof UnwrapNestedRefs<T>]: WritableComputedRef<UnwrapNestedRefs<T>[key]> }
+        for (const key in this.state) {
+            state[key] = computed({
+                get: () => this.getValue(key),
+                set: (value) => this.setValue(key, value)
+            })
         }
+        return state
+    }
+
+    /**
+     * 获取属性响应值 (不建议使用,可能存在对象内部值变更无法同步 Storage 的问题)
+     * @param key 
+     * @returns 
+     */
+    getComputedRef<K extends keyof UnwrapNestedRefs<T>>(key: K) {
+        const state = this.getState()
+        return state[key]
     }
 
-    /** 获取属性响应值 */
-    getRef<K extends keyof T>(key: K): Readonly<Ref<T[K]>> {
-        this.getStorage(key);
-        return readonly(toRef(this.state.value, key));
+    /**
+     * 获取属性响应值 (只读)
+     * @param key 
+     * @returns 
+     */
+    getRef<K extends keyof UnwrapNestedRefs<T>>(key: K) {
+        return readonly(toRef(this.state, key))
     }
 
-    /** 获取属性值 */
-    getValue<K extends keyof T>(key: K): T[K] {
-        this.getStorage(key);
-        return this.state.value[key];
+    /**
+     * 获取属性值
+     * @param key 
+     * @returns 
+     */
+    getValue<K extends keyof UnwrapNestedRefs<T>>(key: K) {
+        return this.state[key]
     }
 
-    /** 设置属性值 */
-    setValue<K extends keyof T>(key: K, value: T[K]) {
+    /**
+     * 设置属性值
+     * @param key 
+     * @param value 
+     */
+    setValue<K extends keyof UnwrapNestedRefs<T>>(key: K, value: UnwrapNestedRefs<T>[K]) {
         if (value === undefined || value === null) {
-            this.clear(key);
+            this.clear(key)
         } else {
-            const str = JSON.stringify(value);
-            this.storage.setItem(key.toString(), str);
-            this.state.value[key] = value;
+            const strValue = JSON.stringify(value)
+            this.storage.setItem(key.toString(), strValue)
+            this.state[key] = value
         }
     }
 
-    /** 清除属性值 */
-    clear<K extends keyof T>(...keys: K[]) {
+    /**
+     * 清除属性值
+     * @param keys 
+     */
+    clear<K extends keyof UnwrapNestedRefs<T>>(...keys: K[]) {
+        const source = reactive({ ...this.source })
         if (keys.length) {
             keys.forEach((key) => {
-                this.storage.removeItem(key.toString());
-                this.state.value[key] = this.source[key];
+                this.storage.removeItem(key.toString())
+                this.state[key] = source[key]
             })
         } else {
-            this.storage.clear();
-            merge(this.state.value, this.source);
+            for (const key in this.state) {
+                this.storage.removeItem(key)
+                this.state[key] = source[key]
+            }
         }
     }
 }