Prechádzať zdrojové kódy

Merge branch 'master' of http://47.101.159.18:3000/Muchinfo/MTP20_WEB_GLOBAL

zhou.xiaoning 2 rokov pred
rodič
commit
a332e9dc74
64 zmenil súbory, kde vykonal 1917 pridanie a 518 odobranie
  1. 4 2
      src/business/performance/index.ts
  2. 2 3
      src/hooks/echarts/candlestick/index.ts
  3. 22 2
      src/hooks/echarts/candlestick/options.ts
  4. 1 0
      src/hooks/echarts/candlestick/types.ts
  5. 2 2
      src/packages/mobile/views/order/performance/components/modify/Index.vue
  6. 1 2
      src/packages/mobile/views/order/position/components/goods/list/Index.vue
  7. 0 0
      src/packages/pc/assets/themes/dark/dark.less
  8. 172 26
      src/packages/pc/assets/themes/default/default.less
  9. 72 0
      src/packages/pc/assets/themes/global/global.less
  10. 3 75
      src/packages/pc/assets/themes/style.less
  11. 26 0
      src/packages/pc/components/base/table-details/index.less
  12. 124 0
      src/packages/pc/components/base/table-details/index.vue
  13. 10 2
      src/packages/pc/components/base/table-filter/index.less
  14. 1 1
      src/packages/pc/components/base/table-filter/index.vue
  15. 30 0
      src/packages/pc/components/base/table/index.less
  16. 8 2
      src/packages/pc/components/base/table/index.vue
  17. 3 2
      src/packages/pc/components/layouts/header/index.less
  18. 1 1
      src/packages/pc/components/layouts/header/index.vue
  19. 2 2
      src/packages/pc/components/layouts/page/index.vue
  20. 0 57
      src/packages/pc/components/modules/echarts-kline/index.less
  21. 0 120
      src/packages/pc/components/modules/echarts-kline/index.vue
  22. 0 23
      src/packages/pc/components/modules/echarts-timeline/index.less
  23. 0 35
      src/packages/pc/components/modules/echarts-timeline/index.vue
  24. 6 3
      src/packages/pc/components/modules/listing/index.vue
  25. 12 6
      src/packages/pc/views/account/address/index.vue
  26. 1 1
      src/packages/pc/views/account/receipt/components/edit/index.vue
  27. 10 8
      src/packages/pc/views/account/receipt/index.vue
  28. 44 0
      src/packages/pc/views/account/sign/components/cancel/index.vue
  29. 0 0
      src/packages/pc/views/account/sign/components/sign/index.vue
  30. 55 2
      src/packages/pc/views/account/sign/index.vue
  31. 1 0
      src/packages/pc/views/footer/goods/delivery/index.vue
  32. 2 3
      src/packages/pc/views/footer/goods/order/cancel/index.vue
  33. 3 2
      src/packages/pc/views/footer/goods/order/index.vue
  34. 115 0
      src/packages/pc/views/footer/goods/position/components/delivery/index.vue
  35. 132 0
      src/packages/pc/views/footer/goods/position/components/transfer/index.vue
  36. 33 5
      src/packages/pc/views/footer/goods/position/index.vue
  37. 42 5
      src/packages/pc/views/footer/performance/buy/index.vue
  38. 81 0
      src/packages/pc/views/footer/performance/components/contracted/index.vue
  39. 68 0
      src/packages/pc/views/footer/performance/components/delay/index.vue
  40. 50 0
      src/packages/pc/views/footer/performance/components/details/index.less
  41. 210 0
      src/packages/pc/views/footer/performance/components/details/index.vue
  42. 182 0
      src/packages/pc/views/footer/performance/components/edit/index.vue
  43. 61 0
      src/packages/pc/views/footer/performance/components/manual/index.vue
  44. 41 3
      src/packages/pc/views/footer/performance/sell/index.vue
  45. 3 2
      src/packages/pc/views/footer/spot/order/index.vue
  46. 8 8
      src/packages/pc/views/footer/spot/position/components/listing/index.vue
  47. 174 31
      src/packages/pc/views/footer/spot/position/components/pickup/index.vue
  48. 21 0
      src/packages/pc/views/market/trade/goods/detail/components/chart/index.less
  49. 6 2
      src/packages/pc/views/market/trade/goods/detail/components/chart/index.vue
  50. 1 2
      src/packages/pc/views/market/trade/goods/detail/components/order/delisting/index.vue
  51. 1 0
      src/packages/pc/views/market/trade/goods/detail/components/order/index.less
  52. 0 41
      src/packages/pc/views/market/trade/goods/detail/index.less
  53. 7 9
      src/packages/pc/views/market/trade/goods/detail/index.vue
  54. 2 2
      src/packages/pc/views/market/trade/spot/index.vue
  55. 5 4
      src/packages/pc/views/market/trade/spot/order/delisting/index.vue
  56. 8 5
      src/packages/pc/views/market/trade/spot/order/index.less
  57. 32 4
      src/packages/pc/views/market/trade/spot/order/index.vue
  58. 2 3
      src/packages/pc/views/market/trade/spot/order/listing/index.vue
  59. 2 2
      src/packages/pc/views/query/capital/history/index.vue
  60. 2 2
      src/packages/pc/views/query/order/goods/history/index.vue
  61. 2 2
      src/packages/pc/views/query/order/spot/history/index.vue
  62. 2 2
      src/packages/pc/views/query/trade/goods/history/index.vue
  63. 2 2
      src/packages/pc/views/query/trade/spot/history/index.vue
  64. 4 0
      src/types/model/performance.d.ts

+ 4 - 2
src/business/performance/index.ts

@@ -71,7 +71,9 @@ export function usePerformanceManualConfirm(buyaccountid: number) {
 export function usePerformanceModifyContact() {
     const loading = shallowRef(false)
 
-    const formData = ref<Partial<Proto.PerformanceModifyContactReq>>({ })
+    const formData = ref<Partial<Proto.PerformanceModifyContactReq>>({ 
+        AccountID: accountStore.accountId
+    })
 
     const formSubmit = async () => {
         try {
@@ -119,4 +121,4 @@ export function usePerformanceDelayApply() {
         formData,
         formSubmit,
     }
-}
+}

+ 2 - 3
src/hooks/echarts/candlestick/index.ts

@@ -15,7 +15,6 @@ export function useCandlestickChart(goodscode: string) {
     const loading = ref(false);
     const isEmpty = ref(true);
     const dataIndex = ref(-1); // 当前数据索引值
-    const cycleType = ref(ChartCycleType.Minutes);
     const quote = futuresStore.getQuoteInfo(goodscode); // 实时行情
 
     // 当前选中的数据项
@@ -58,7 +57,7 @@ export function useCandlestickChart(goodscode: string) {
     const initData = (cycletype: ChartCycleType) => {
         clearData();
         dataIndex.value = -1;
-        cycleType.value = cycletype;
+        options.cycleType = cycletype;
         loading.value = true;
         isEmpty.value = true;
 
@@ -88,7 +87,7 @@ export function useCandlestickChart(goodscode: string) {
      */
     const getCycleMilliseconds = () => {
         const milliseconds = 60 * 1000; // 一分钟毫秒数
-        switch (cycleType.value) {
+        switch (options.cycleType) {
             case ChartCycleType.Minutes5: {
                 return milliseconds * 5;
             }

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

@@ -1,6 +1,7 @@
 import { reactive, watch } from 'vue'
 import { ECOption } from '@/components/base/echarts/core'
 import { timerInterceptor } from '@/utils/timer'
+import { ChartCycleType } from '@/constants/chart'
 import { useGlobalStore } from '@/stores'
 import { EchartsDataset, EchartsOptions, Colors } from './types'
 import moment from 'moment'
@@ -33,6 +34,7 @@ function getColors() {
 export function useOptions(dataset: EchartsDataset) {
     // 图表配置项
     const options = reactive<EchartsOptions>({
+        cycleType: ChartCycleType.Minutes,
         colors: getColors(),
         candlestick: {},
         macd: {},
@@ -68,12 +70,30 @@ export function useOptions(dataset: EchartsDataset) {
             xAxis: {
                 type: 'category',
                 axisLabel: {
-                    formatter: (val: string) => moment(val).format('YYYY-MM-DD'),
+                    formatter: (val: string) => {
+                        switch (options.cycleType) {
+                            case ChartCycleType.Day: {
+                                return moment(val).format('YYYY-MM-DD')
+                            }
+                            default: {
+                                return moment(val).format('HH:mm')
+                            }
+                        }
+                    },
                     margin: 12,
                 },
                 axisPointer: {
                     label: {
-                        formatter: (params) => moment(params.value).format('YYYY-MM-DD HH:mm:ss'),
+                        formatter: (params) => {
+                            switch (options.cycleType) {
+                                case ChartCycleType.Day: {
+                                    return moment(params.value).format('YYYY-MM-DD')
+                                }
+                                default: {
+                                    return moment(params.value).format('YYYY-MM-DD HH:mm:ss')
+                                }
+                            }
+                        },
                     }
                 },
                 axisTick: {

+ 1 - 0
src/hooks/echarts/candlestick/types.ts

@@ -77,6 +77,7 @@ export type CCI = {
  * 图表配置项
  */
 export interface EchartsOptions {
+    cycleType: Number; // 周期类型
     colors: Colors;
     candlestick: ECOption;
     macd: ECOption;

+ 2 - 2
src/packages/mobile/views/order/performance/components/modify/Index.vue

@@ -130,8 +130,8 @@ const contracted = () => {
         formData.value.PerformancePlanID = props.selectedRow.performanceplanid
         /// Json String
         const json =  {
-            ContactInfo: '${Contract.value}',
-            ReceiveInfo: '${Receive.value}',
+            ContactInfo: `${Contract.value}`,
+            ReceiveInfo: `${Receive.value}`,
             ReceiptInfo: `${Receipt.value}`
         }
         formData.value.ContactInfo= JSON.stringify(json)

+ 1 - 2
src/packages/mobile/views/order/position/components/goods/list/Index.vue

@@ -65,7 +65,7 @@ import { queryTradePosition } from '@/services/api/order'
 import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
 import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
 import { formatDecimal, handleNumberValue } from '@/filters'
-import { useUserStore, useFuturesStore } from '@/stores'
+import { useFuturesStore } from '@/stores'
 import { getAmountColor } from '@/utils/vant';
 
 const componentMap = new Map<string, unknown>([
@@ -73,7 +73,6 @@ const componentMap = new Map<string, unknown>([
     ['delivery', defineAsyncComponent(() => import('../delivery/Index.vue'))]
 ])
 
-const userStore = useUserStore()
 const futuresStore = useFuturesStore()
 
 const dataList = shallowRef<Model.TradePositionRsp[]>([])

+ 0 - 0
src/packages/pc/assets/themes/dark/variable.less → src/packages/pc/assets/themes/dark/dark.less


+ 172 - 26
src/packages/pc/assets/themes/default/variable.less → src/packages/pc/assets/themes/default/default.less

@@ -1,4 +1,4 @@
-[theme='default'] {
+:root {
     /* 字体大小规范 */
     --font-x-large: 18px;
     --font-large: 16px;
@@ -31,21 +31,96 @@
     --sidebar-menu-item-hover: #3a87f7;
     --sidebar-menu-item-active: #3a87f7;
 
-    .el-select {
-        --el-select-border-color-hover: #45535e;
-        --el-select-input-focus-border-color: var(--el-select-border-color-hover);
-    }
+    --el-button-text-color: #fff;
+    --el-button-bg-color: #7a8a94;
+
+    --el-color-primary: #26487c;
+    --el-color-primary-light-3: #29538c;
+    --el-color-primary-light-5: #70abd8;
+    --el-color-primary-light-7: #c6e2ff;
+    --el-color-primary-light-8: #d9ecff;
+    --el-color-primary-light-9: #ecf5ff;
+    --el-color-primary-dark-2: var(--el-color-primary);
+
+    --el-color-info: #405066;
+    --el-color-info-light-3: #455773;
+    --el-color-info-dark-2: var(--el-color-info);
+
+    --el-color-danger: #db5050;
+    --el-color-danger-light-3: #e56060;
+    --el-color-danger-dark-2: var(--el-color-danger);
+
+    //--el-bg-color-overlay: #424E59;
 
     .el-form {
-        &--vertical {
-            .el-input {
+        &-item {
+            &__label {
+                color: #7a8a94;
+            }
+
+            &--col {
+                display: flex;
+                width: 100%;
+                gap: 10px;
+            }
+        }
+
+        &--filter {
+
+            .el-input,
+            .el-select,
+            .el-date-editor {
                 --el-input-bg-color: #252d34;
                 --el-input-border-color: var(--el-input-bg-color);
-                --el-input-hover-border-color: #45535e;
+                --el-input-hover-border-color: #35434f;
+                --el-input-focus-border-color: var(--el-input-hover-border-color);
+                --el-select-border-color-hover: var(--el-input-hover-border-color);
+                --el-select-input-focus-border-color: var(--el-input-hover-border-color);
+                --el-input-text-color: var(--color-default);
+                --el-text-color-primary: #fff;
+                --el-text-color-placeholder: #4f5f6c;
+                --el-text-color-regular: var(--el-text-color-primary);
+            }
+        }
+
+        &--vertical,
+        &--horizontal {
+            .el-form-item {
+                &:last-child {
+                    margin-bottom: 18px;
+                }
+            }
+
+            .el-input,
+            .el-select,
+            .el-textarea {
+                --el-input-bg-color: #15202b;
+                --el-input-border-color: #2b3f52;
+                --el-input-hover-border-color: #1890ff;
                 --el-input-focus-border-color: var(--el-input-hover-border-color);
+                --el-select-border-color-hover: var(--el-input-hover-border-color);
+                --el-select-input-focus-border-color: var(--el-input-hover-border-color);
                 --el-input-text-color: var(--color-default);
                 --el-text-color-placeholder: #4f5f6c;
             }
+
+            .el-select {
+                width: 100%;
+            }
+
+            .el-input-number {
+                width: 100%;
+
+                &__decrease,
+                &__increase {
+                    &:hover {
+                        color: #1890ff;
+                    }
+
+                    background-color: transparent;
+                    border: 0;
+                }
+            }
         }
 
         &--horizontal {
@@ -69,24 +144,6 @@
                 &--row {
                     width: 100%;
                 }
-
-                &--col {
-                    display: flex;
-                    width: 100%;
-                    gap: 10px;
-                }
-            }
-
-            .el-input {
-                --el-input-bg-color: #15202b;
-                --el-input-border-color: #2b3f52;
-                --el-input-hover-border-color: var(--el-color-primary);
-                --el-input-text-color: var(--color-default);
-                --el-text-color-placeholder: #4f5f6c;
-            }
-
-            .el-select {
-                width: 100%;
             }
         }
     }
@@ -187,4 +244,93 @@
             }
         }
     }
+
+    .g-view-detail {
+        &__header {
+            display: flex;
+            align-items: center;
+            gap: 10px;
+            width: 100%;
+            background-color: #181e22;
+            border-bottom: 1px solid #3a87f7;
+            padding: 4px;
+
+            .iconbar {
+                .el-button.is-link {
+                    padding: 5px 10px;
+                }
+            }
+
+            .breadcrumb {
+                --item-background: #1556b5;
+                display: flex;
+                color: #fff;
+
+                li {
+                    display: flex;
+                    align-items: center;
+
+                    &:first-child::before {
+                        border-left-color: var(--item-background);
+                        border-top-left-radius: 4px;
+                        border-bottom-left-radius: 4px;
+                    }
+
+                    &::before {
+                        content: '';
+                        width: 0;
+                        height: 0;
+                        border: 0 solid var(--item-background);
+                        border-width: 16px 0 16px 12px;
+                        border-left-color: transparent;
+                    }
+
+                    &::after {
+                        content: '';
+                        width: 0;
+                        height: 0;
+                        border: 0 solid transparent;
+                        border-width: 16px 0 16px 12px;
+                        border-left-color: var(--item-background);
+                    }
+
+                    span {
+                        display: flex;
+                        justify-content: center;
+                        align-items: center;
+                        height: 32px;
+                        background-color: var(--item-background);
+                        padding-left: 10px;
+
+                        &:last-child {
+                            padding-right: 10px;
+                        }
+                    }
+                }
+            }
+
+            .datainfo {
+                display: flex;
+                gap: 20px;
+                color: #7A8A94;
+                font-size: 12px;
+
+                li {
+                    span {
+                        &:last-child {
+                            color: #fff;
+                        }
+                    }
+                }
+            }
+
+            .buttonbar {
+                margin-left: auto;
+
+                .el-button {
+                    min-width: 88px;
+                }
+            }
+        }
+    }
 }

+ 72 - 0
src/packages/pc/assets/themes/global/global.less

@@ -0,0 +1,72 @@
+[class*='g-image'] {
+    position: relative;
+    object-fit: cover;
+    overflow: hidden;
+
+    &:before {
+        content: '';
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 100%;
+        height: 100%;
+        background: url("~@pc/assets/images/avatar.png") no-repeat center;
+        background-size: cover;
+    }
+}
+
+.g-price-up {
+    color: #ff3333;
+}
+
+.g-price-normal {
+    color: #333333;
+}
+
+.g-price-down {
+    color: #0baf1f;
+}
+
+.el-form {
+    &-item:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.el-popper {
+    &.is-light {
+        border: 0;
+    }
+
+    .el-menu {
+        border-radius: 4px;
+        box-shadow: 0 2px 30px 0 rgba(0, 0, 0, .15);
+
+        &--popup {
+            min-width: 160px;
+        }
+
+        &--vertical {
+            .el-menu {
+                &-item {
+                    height: 44px;
+                    line-height: 44px;
+                    color: #666;
+
+                    &.is-active {
+                        color: var(--sidebar-menu-item-active);
+                    }
+                }
+            }
+        }
+    }
+}
+
+.el-dropdown-menu {
+    padding: 10px 0;
+
+    &__item {
+        line-height: 36px;
+        padding: 0 20px;
+    }
+}

+ 3 - 75
src/packages/pc/assets/themes/style.less

@@ -1,76 +1,4 @@
 @import './base/reset.less';
-@import './default/variable.less';
-@import './dark/variable.less';
-
-[class*='g-image'] {
-    position: relative;
-    object-fit: cover;
-    overflow: hidden;
-
-    &:before {
-        content: '';
-        position: absolute;
-        left: 0;
-        top: 0;
-        width: 100%;
-        height: 100%;
-        background: url("~@pc/assets/images/avatar.png") no-repeat center;
-        background-size: cover;
-    }
-}
-
-.el-form {
-    &-item:last-child {
-        margin-bottom: 0;
-    }
-}
-
-.el-popper {
-    &.is-light {
-        border: 0;
-    }
-
-    .el-menu {
-        border-radius: 4px;
-        box-shadow: 0 2px 30px 0 rgba(0, 0, 0, .15);
-
-        &--popup {
-            min-width: 160px;
-        }
-
-        &--vertical {
-            .el-menu {
-                &-item {
-                    height: 44px;
-                    line-height: 44px;
-                    color: #666;
-
-                    &.is-active {
-                        color: var(--sidebar-menu-item-active);
-                    }
-                }
-            }
-        }
-    }
-}
-
-.el-dropdown-menu {
-    padding: 10px 0;
-
-    &__item {
-        line-height: 36px;
-        padding: 0 20px;
-    }
-}
-
-.g-price-up {
-    color: #ff3333;
-}
-
-.g-price-normal {
-    color: #333333;
-}
-
-.g-price-down {
-    color: #0baf1f;
-}
+@import './global/global.less';
+@import './default/default.less';
+@import './dark/dark.less';

+ 26 - 0
src/packages/pc/components/base/table-details/index.less

@@ -0,0 +1,26 @@
+.app-table-details {
+    &:not(:first-child) {
+        margin-top: 20px;
+    }
+
+    >table {
+        table-layout: fixed;
+        border-collapse: collapse;
+
+        th,
+        td {
+            border: 0 solid #ebeef5;
+            padding: 6px 10px;
+        }
+
+        th {
+            color: #666;
+            font-weight: normal;
+            vertical-align: top;
+        }
+
+        td {
+            vertical-align: top;
+        }
+    }
+}

+ 124 - 0
src/packages/pc/components/base/table-details/index.vue

@@ -0,0 +1,124 @@
+<template>
+    <div class="app-table-details g-details">
+        <div class="g-details__title">
+            <slot name="title">
+                {{ title }}
+            </slot>
+        </div>
+        <table cellspacing="0" cellpadding="0" :style="tableStyle">
+            <tbody>
+                <tr v-for="(items, i) in cellGroup" :key="i">
+                    <template v-for="(item, n) in items" :key="n">
+                        <th :style="labelStyle">
+                            <slot :name="item.prop + 'Label'">
+                                {{ item.label }}
+                            </slot>
+                        </th>
+                        <td :colspan="(column * 2 - items.length * 2) + 1" :style="valueStyle">
+                            <slot :name="item.prop" :value="data[item.prop]">
+                                {{ handleValue(item) }}
+                            </slot>
+                        </td>
+                    </template>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { PropType, computed } from 'vue'
+import { handleNoneValue } from '@/filters'
+
+interface cellProp {
+    prop: string;
+    label: string;
+    decimal?: number; // 保留小数点位数
+    entireRow?: boolean; // 是否整行显示
+    hideEmpty?: boolean; // 是否隐藏空值
+}
+
+const props = defineProps({
+    title: String,
+    data: {
+        type: Object,
+        required: true
+    },
+    cellProps: {
+        type: Array as PropType<cellProp[]>,
+        required: true
+    },
+    column: {
+        type: Number,
+        default: 1
+    },
+    width: {
+        type: [String, Number],
+        default: '100%'
+    },
+    // 标签宽度
+    labelWidth: {
+        type: Number,
+        default: 0
+    },
+    // 标签对齐方式
+    labelAlign: {
+        type: String as PropType<'left' | 'center' | 'right'>,
+        default: 'right'
+    },
+    // 边框大小
+    borderWidth: {
+        type: Number,
+        default: 0
+    },
+})
+
+const cellGroup = computed(() => {
+    // const result = []
+    // let index = 0
+    // // 一维数组转换成二维数组
+    // while (index < props.cellProps.length) {
+    //     const group = props.cellProps.slice(index, index += props.column)
+    //     result.push(group)
+    // }
+    return props.cellProps.reduce((pre, cur) => {
+        const last = pre[pre.length - 1]
+        if (last) {
+            if (cur.entireRow || last.length === props.column) {
+                pre[pre.length] = [cur]
+            } else {
+                last.push(cur)
+            }
+        } else {
+            pre[0] = [cur]
+        }
+        return pre
+    }, [] as cellProp[][])
+})
+
+const tableStyle = computed(() => ({
+    width: typeof props.width === 'number' ? props.width + 'px' : props.width
+}))
+
+const valueStyle = computed(() => ({
+    borderWidth: props.borderWidth + 'px',
+}))
+
+const labelStyle = computed(() => ({
+    width: props.labelWidth ? props.labelWidth + 'px' : 'fit-content',
+    textAlign: props.labelAlign,
+    ...valueStyle.value,
+}))
+
+const handleValue = (cell: cellProp) => {
+    const value = props.data[cell.prop]
+    if (Number.isFinite(value) && cell.decimal) {
+        return value.toFixed(cell.decimal)
+    }
+    return handleNoneValue(value)
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 10 - 2
src/packages/pc/components/base/table-filter/index.less

@@ -1,7 +1,7 @@
 .app-table-filter {
-    display  : flex;
+    display: flex;
     flex-wrap: wrap;
-    gap      : 12px;
+    gap: 12px;
 
     .el-form-item {
         margin: 0;
@@ -16,4 +16,12 @@
     .el-input {
         width: 160px;
     }
+
+    .el-button {
+        min-width: 80px;
+    }
+
+    .el-date-editor.el-input__wrapper {
+        width: 300px;
+    }
 }

+ 1 - 1
src/packages/pc/components/base/table-filter/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-form class="app-table-filter">
+  <el-form class="el-form--filter app-table-filter">
     <el-form-item v-if="$slots.before">
       <slot name="before"></slot>
     </el-form-item>

+ 30 - 0
src/packages/pc/components/base/table/index.less

@@ -82,4 +82,34 @@
             }
         }
     }
+
+    .el-table {
+
+        &.buy,
+        &.sell {
+            --el-table-row-hover-bg-color: var(--el-table-bg-color); // 表格行鼠标经过背景色
+            --el-table-current-row-bg-color: var(--el-table-bg-color); // 当前行高亮颜色
+
+            td.el-table {
+
+                &__cell:not(&__expanded-cell):first-child,
+                &__cell:is(&__expand-column)+td {
+                    background-color: var(--el-table-header-bg-color);
+                }
+
+                &__expanded-cell,
+                &__expanded-cell:hover {
+                    background-color: #000 !important;
+                }
+            }
+        }
+
+        &.buy {
+            --el-table-bg-color: #271112; //表格背景颜色
+        }
+
+        &.sell {
+            --el-table-bg-color: #10251d; //表格背景颜色
+        }
+    }
 }

+ 8 - 2
src/packages/pc/components/base/table/index.vue

@@ -14,8 +14,9 @@
       </div>
     </div>
     <div class="app-table__container">
-      <el-table ref="tableRef" height="100%" :header-cell-class-name="selectionType" v-bind="$attrs" highlight-current-row
-        scrollbar-always-on @row-click="onRowClick" @expand-change="onRowClick" @select="onSelect" border>
+      <el-table ref="tableRef" height="100%" :header-cell-class-name="selectionType" v-bind="$attrs"
+        :highlight-current-row="highlightCurrentRow" scrollbar-always-on @row-click="onRowClick"
+        @expand-change="onRowClick" @select="onSelect" border>
         <!-- 展开行 -->
         <el-table-column type="expand" v-if="$slots.expand">
           <template #default="{ row, $index }">
@@ -68,6 +69,11 @@ export default defineComponent({
     showToolbar: Boolean,
     loading: Boolean,
     showIndex: Boolean,
+    // 是否要高亮当前行
+    highlightCurrentRow: {
+      type: Boolean,
+      default: true
+    },
     // 选择列类型
     selectionType: {
       type: String as PropType<'single' | 'multiple'>,

+ 3 - 2
src/packages/pc/components/layouts/header/index.less

@@ -49,6 +49,7 @@
         }
 
         .user-dropdown {
+            color: #7a8a94;
             margin-left: 20px;
 
             &__link {
@@ -59,8 +60,8 @@
             }
 
             .g-image--avatar {
-                width: 32px;
-                height: 32px;
+                width: 24px;
+                height: 24px;
                 border-radius: 50%;
                 font-size: 0;
                 margin-right: 4px;

+ 1 - 1
src/packages/pc/components/layouts/header/index.vue

@@ -29,7 +29,7 @@
 
 <script lang="ts" setup>
 import { ref, onMounted, computed } from 'vue'
-import { ArrowRight, SwitchButton } from '@element-plus/icons-vue'
+import { SwitchButton } from '@element-plus/icons-vue'
 import { getFileUrl } from '@/filters'
 import { useUserStore, useGlobalStore } from '@/stores'
 import eventBus from '@/services/bus'

+ 2 - 2
src/packages/pc/components/layouts/page/index.vue

@@ -2,11 +2,11 @@
   <div class="app-page">
     <div class="app-page__header">
       <app-header>
-        <template #left>
+        <!-- <template #left>
           <i class="icon-fold-expand" title="折叠/展开" @click="isCollapse = !isCollapse">
             <span :class="isCollapse ? 'g-icon--expand' : 'g-icon--fold'"></span>
           </i>
-        </template>
+        </template> -->
       </app-header>
     </div>
     <div class="app-page__wrapper">

+ 0 - 57
src/packages/pc/components/modules/echarts-kline/index.less

@@ -1,57 +0,0 @@
-.app-echats-kline {
-    display       : flex;
-    flex-direction: column;
-    height        : 100%;
-
-    &__tip {
-        margin: auto;
-    }
-
-    &__container {
-        display       : flex;
-        flex-direction: column;
-
-        .app-echarts {
-            flex: 1;
-        }
-
-        .legend {
-            display  : flex;
-            flex-wrap: wrap;
-            color    : #7a8a94;
-            font-size: 12px;
-            padding  : 10px;
-            margin   : 0;
-
-            &-item {
-                &:not(:first-child) {
-                    margin-left: 10px;
-                }
-            }
-        }
-    }
-
-    &__container.main {
-        flex: 2;
-    }
-
-    &__container.indicator {
-        position: relative;
-        flex    : 1.2;
-
-        .section {
-            flex          : 1;
-            display       : flex;
-            flex-direction: column;
-            overflow      : hidden;
-
-            &.is-hide {
-                position: absolute;
-                z-index : -1;
-                width   : 100%;
-                height  : 100%;
-                opacity : 0;
-            }
-        }
-    }
-}

+ 0 - 120
src/packages/pc/components/modules/echarts-kline/index.vue

@@ -1,120 +0,0 @@
-<template>
-  <div class="app-echats-kline">
-    <template v-if="loading">
-      <div class="app-echats-kline__tip">正在加载...</div>
-    </template>
-    <template v-else-if="isEmpty">
-      <div class="app-echats-kline__tip">暂无数据</div>
-    </template>
-    <template v-else>
-      <div class="app-echats-kline__container main">
-        <ul class="legend">
-          <li class="legend-item">开: {{ selectedItem.open }}</li>
-          <li class="legend-item">收: {{ selectedItem.close }}</li>
-          <li class="legend-item">高: {{ selectedItem.highest }}</li>
-          <li class="legend-item">低: {{ selectedItem.lowest }}</li>
-          <li class="legend-item">MA5: {{ selectedItem.ma5 }}</li>
-          <li class="legend-item">MA10: {{ selectedItem.ma10 }}</li>
-          <li class="legend-item">MA15: {{ selectedItem.ma15 }}</li>
-        </ul>
-        <app-echarts :option="options.candlestick" v-model:dataIndex="dataIndex" @ready="mainReady" />
-      </div>
-      <app-tabs class="app-tabs--info" :data-list="chartSeriesTypeList" @change="tabChange" v-if="showIndicator">
-        <div class="app-echats-kline__container indicator">
-          <!-- MACD -->
-          <section class="section" v-if="activeSeriesType === ChartSeriesType.MACD">
-            <ul class="legend">
-              <li class="legend-item">MACD: {{ selectedItem.macd }}</li>
-              <li class="legend-item">DIF: {{ selectedItem.dif }}</li>
-              <li class="legend-item">DEA: {{ selectedItem.dea }}</li>
-            </ul>
-            <app-echarts :option="options.macd" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
-          </section>
-          <!-- VOL -->
-          <section class="section" v-if="activeSeriesType === ChartSeriesType.VOL">
-            <ul class="legend">
-              <li class="legend-item">VOL: {{ selectedItem.vol }}</li>
-            </ul>
-            <app-echarts :option="options.vol" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
-          </section>
-          <!-- KDJ -->
-          <section class="section" v-if="activeSeriesType === ChartSeriesType.KDJ">
-            <ul class="legend">
-              <li class="legend-item">K: {{ selectedItem.k }}</li>
-              <li class="legend-item">D: {{ selectedItem.d }}</li>
-              <li class="legend-item">J: {{ selectedItem.j }}</li>
-            </ul>
-            <app-echarts :option="options.kdj" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
-          </section>
-          <!-- CCI -->
-          <section class="section" v-if="activeSeriesType === ChartSeriesType.CCI">
-            <ul class="legend">
-              <li class="legend-item">CCI: {{ selectedItem.cci }}</li>
-            </ul>
-            <app-echarts :option="options.cci" v-model:dataIndex="dataIndex" @ready="indicatorReady" />
-          </section>
-        </div>
-      </app-tabs>
-    </template>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import { ref, PropType, watch } from 'vue'
-import { echarts } from '@/components/base/echarts/core'
-import { ChartCycleType, ChartSeriesType, getChartSeriesTypeList } from '@/constants/chart'
-import { useCandlestickChart } from '@/hooks/echarts/candlestick'
-import AppEcharts from '@/components/base/echarts/index.vue'
-import AppTabs from '@/components/base/tabs/index.vue'
-
-const props = defineProps({
-  goodscode: {
-    type: String,
-    required: true,
-  },
-  // 周期类型
-  cycleType: {
-    type: Number as PropType<ChartCycleType>,
-    default: ChartCycleType.Minutes,
-  },
-  // 是否显示指标
-  showIndicator: {
-    type: Boolean,
-    default: true,
-  },
-})
-
-const { loading, dataIndex, isEmpty, options, selectedItem, initData, initOptions } = useCandlestickChart(props.goodscode);
-const activeSeriesType = ref(ChartSeriesType.MACD); // 当前选中的指标
-const chartGroup = new Map<string, echarts.ECharts>(); // 图表联动实例组
-const chartSeriesTypeList = getChartSeriesTypeList();
-
-// 指标切换
-const tabChange = (index: number) => {
-  activeSeriesType.value = chartSeriesTypeList[index].value;
-  setTimeout(() => {
-    initOptions();
-  }, 0);
-}
-
-const mainReady = (chart: echarts.ECharts) => {
-  chartGroup.set('main', chart);
-  initOptions();
-}
-
-const indicatorReady = (chart: echarts.ECharts) => {
-  chartGroup.set('indicator', chart);
-  echarts.connect([...chartGroup.values()]); // 图表联动
-}
-
-// 监听周期选择变化
-watch(() => props.cycleType, (val) => {
-  initData(val);
-}, {
-  immediate: true
-})
-</script>
-
-<style lang="less">
-@import './index.less';
-</style>

+ 0 - 23
src/packages/pc/components/modules/echarts-timeline/index.less

@@ -1,23 +0,0 @@
-.app-echats-timeline {
-    display       : flex;
-    flex-direction: column;
-    height        : 100%;
-
-    &__tip {
-        margin: auto;
-    }
-
-    .legend {
-        display  : flex;
-        color    : #7a8a94;
-        font-size: 12px;
-        padding  : 10px;
-        margin   : 0;
-
-        &-item {
-            &:not(:first-child) {
-                margin-left: 10px;
-            }
-        }
-    }
-}

+ 0 - 35
src/packages/pc/components/modules/echarts-timeline/index.vue

@@ -1,35 +0,0 @@
-<template>
-  <div class="app-echats-timeline">
-    <template v-if="loading">
-      <div class="app-echats-timeline__tip">正在加载...</div>
-    </template>
-    <template v-else-if="isEmpty">
-      <div class="app-echats-timeline__tip">暂无数据</div>
-    </template>
-    <template v-else>
-      <ul class="legend">
-        <li class="legend-item">收: {{ selectedItem.close }}</li>
-        <li class="legend-item">MA5: {{ selectedItem.ma5 }}</li>
-      </ul>
-      <app-echarts :option="options.timeline" v-model:dataIndex="dataIndex" @ready="initOptions" />
-    </template>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import { useTimelineChart } from '@/hooks/echarts/timeline'
-import AppEcharts from '@/components/base/echarts/index.vue'
-
-const props = defineProps({
-  goodscode: {
-    type: String,
-    required: true,
-  },
-})
-
-const { loading, dataIndex, isEmpty, options, selectedItem, initOptions } = useTimelineChart(props.goodscode);
-</script>
-
-<style lang="less">
-@import './index.less';
-</style>

+ 6 - 3
src/packages/pc/components/modules/listing/index.vue

@@ -5,7 +5,8 @@
         </div>
         <div class="app-listing__form">
             <h4 class="block-title">订单挂牌</h4>
-            <el-form ref="formRef" size="small" label-width="50px" :model="formData" :rules="formRules">
+            <el-form ref="formRef" class="el-form--vertical" size="small" label-width="50px" :model="formData"
+                :rules="formRules">
                 <el-form-item prop="GoodsID" label="商品">
                     <el-select placeholder="请选择" v-model="goodsStore.goodsId">
                         <el-option :label="item.goodsname" :value="item.goodsid"
@@ -24,8 +25,10 @@
                         v-model="formData.OrderPrice" />
                 </el-form-item>
                 <el-form-item prop="OrderQty" label="数量">
-                    <el-input-number placeholder="请输入" :min="agreeunit" :step="qtyStep" v-model="orderQty" />
-                    <span>{{ getGoodsUnitName(quote?.goodunitid) }}</span>
+                    <div class="el-form-item--col">
+                        <el-input-number placeholder="请输入" :min="agreeunit" :step="qtyStep" v-model="orderQty" />
+                        <span>{{ getGoodsUnitName(quote?.goodunitid) }}</span>
+                    </div>
                     <el-radio-group v-model="qtyStep" @change="onRadioChange">
                         <el-radio-button v-for="(value, index) in qtyStepList" :key="index" :label="value" />
                     </el-radio-group>

+ 12 - 6
src/packages/pc/views/account/address/index.vue

@@ -1,8 +1,9 @@
 <!-- 账户管理-收货地址管理 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey" :expand-row-keys="expandKeys"  @row-click="rowClick">
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey"
+        :expand-row-keys="expandKeys" @row-click="rowClick">
         <template #header>
-            <el-button type="danger" size="small" @click="openComponent('edit')">新增收货地址</el-button>
+            <app-filter :options="filterOptons" />
         </template>
         <!-- 证件类型 -->
         <template #cardtypeid="{ value }">
@@ -14,7 +15,7 @@
         </template>
         <!-- 是否默认 -->
         <template #isdefault="{ value }">
-            {{ value? '是': '否' }}
+            {{ value ? '是' : '否' }}
         </template>
         <!-- 操作 -->
         <template #expand>
@@ -26,7 +27,7 @@
         </template>
     </app-table>
     <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
-            v-if="componentId" />
+        v-if="componentId" />
 </template>
 
 <script lang="ts" setup>
@@ -35,8 +36,10 @@ import { useRequest } from '@/hooks/request'
 import { queryUserReceiveInfo } from '@/services/api/user'
 import { getCertificateTypeName } from '@/constants/certificate'
 import { useComponent } from '@/hooks/component'
+import { useDataFilter } from '@/hooks/datatable'
 import { useComposeTable } from '@pc/components/base/table'
 import AppTable from '@pc/components/base/table/index.vue'
+import AppFilter from '@pc/components/base/table-filter/index.vue'
 
 const componentMap = new Map<string, unknown>([
     ['edit', defineAsyncComponent(() => import('./components/edit/index.vue'))],
@@ -45,8 +48,8 @@ const componentMap = new Map<string, unknown>([
 ])
 
 const { rowKey, expandKeys, selectedRow, rowClick } = useComposeTable<Model.UserReceiveInfoRsp>({ rowKey: 'autoid' })
-
-const { loading, dataList, run } = useRequest(queryUserReceiveInfo, {})
+const { filterOptons } = useDataFilter<Model.UserReceiveInfoReq>()
+const { loading, dataList, run } = useRequest(queryUserReceiveInfo)
 
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
     run()
@@ -61,4 +64,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'isdefault', label: '是否默认' },
 ])
 
+filterOptons.buttonList = [
+    { lable: '新增地址', className: 'el-button--primary', onClick: () => openComponent('edit') },
+]
 </script>

+ 1 - 1
src/packages/pc/views/account/receipt/components/edit/index.vue

@@ -2,7 +2,7 @@
 <template>
     <app-drawer :title="selectedRow?.autoid ? '修改发票' : '新增发票'" :width="460" v-model:show="show" :loading="loading"
         :refresh="refresh">
-        <el-form ref="formRef" label-width="100px" :model="formData" :rules="formRules">
+        <el-form ref="formRef" class="el-form--vertical" label-width="100px" :model="formData" :rules="formRules">
             <el-form-item label="发票类型" prop="ReceiptType">
                 <el-radio-group v-model="formData.ReceiptType">
                     <el-radio :label="1">个人</el-radio>

+ 10 - 8
src/packages/pc/views/account/receipt/index.vue

@@ -1,8 +1,9 @@
 <!-- 账户管理-发票信息管理 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey" :expand-row-keys="expandKeys"  @row-click="rowClick">
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey"
+        :expand-row-keys="expandKeys" @row-click="rowClick">
         <template #header>
-            <el-button type="danger" size="small" @click="openComponent('edit')">新增收货地址</el-button>
+            <app-filter :options="filterOptons" />
         </template>
         <!-- 发票类型 -->
         <template #receipttype="{ value }">
@@ -24,10 +25,12 @@
 import { shallowRef, defineAsyncComponent } from 'vue'
 import { getReceiptTypeName } from '@/constants/receipt'
 import { useComponent } from '@/hooks/component'
+import { useDataFilter } from '@/hooks/datatable'
 import { useComposeTable } from '@pc/components/base/table'
 import { useRequest } from '@/hooks/request'
 import { queryWrUserReceiptInfo } from '@/services/api/user'
 import AppTable from '@pc/components/base/table/index.vue'
+import AppFilter from '@pc/components/base/table-filter/index.vue'
 
 const componentMap = new Map<string, unknown>([
     ['edit', defineAsyncComponent(() => import('./components/edit/index.vue'))],
@@ -35,12 +38,8 @@ const componentMap = new Map<string, unknown>([
 ])
 
 const { rowKey, expandKeys, rowClick, selectedRow } = useComposeTable<Model.WrUserReceiptInfoRsp>({ rowKey: 'autoid' })
-
-defineProps({
-    code: String
-})
-
-const { loading, dataList, run } = useRequest(queryWrUserReceiptInfo, {})
+const { filterOptons } = useDataFilter<Model.WrUserReceiptInfoReq>()
+const { loading, dataList, run } = useRequest(queryWrUserReceiptInfo)
 
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
     run()
@@ -54,4 +53,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'information', label: '企业信息' },
 ])
 
+filterOptons.buttonList = [
+    { lable: '新增发票', className: 'el-button--primary', onClick: () => openComponent('edit') },
+]
 </script>

+ 44 - 0
src/packages/pc/views/account/sign/components/cancel/index.vue

@@ -0,0 +1,44 @@
+<!-- 账户管理-签约账号管理-解约 -->
+<template>
+    <app-drawer title="提示" v-model:show="show" :loading="loading" :refresh="refresh">
+        <div>确认要解约吗?</div>
+        <template #footer>
+            <el-button type="primary" @click="onCancelSumit()">提交</el-button>
+            <el-button @click="onCancel(false)" plain>取消</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage } from 'element-plus'
+import { useDoCancelBankSign } from '@/business/bank'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.BankAccountSignRsp>,
+        required: true
+    }
+})
+
+const { cancelSubmit, loading } = useDoCancelBankSign()
+
+const show = ref(true)
+const refresh = ref(false)
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onCancelSumit = () => {
+    /// 提交
+    cancelSubmit().then(() => {
+        ElMessage.success('提交成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('提交失败:' + err)
+    })
+}
+</script>

+ 0 - 0
src/packages/pc/views/account/sign/components/sign/index.vue


+ 55 - 2
src/packages/pc/views/account/sign/index.vue

@@ -1,12 +1,65 @@
 <!-- 签约账号管理 -->
 <template>
-    <app-table :data="[]" v-model:columns="tableColumns" />
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey" :expand-row-keys="expandKeys"  @row-click="rowClick">
+        <template #header>
+            <el-button type="danger" size="small" @click="openComponent('')">添加签约账户</el-button>
+        </template>
+        <!-- 证件类型 -->
+        <template #cardtype="{ value }">
+            {{ getCertificateTypeCodeName(value) }}
+        </template>
+        <!-- 状态 -->
+        <template #signstatus="{ value }">
+            {{ getSignStatusName(value) }}
+        </template>
+        <!-- 展开行 -->
+        <template #expand="{ row }">
+            <div class="buttonbar">
+                <el-button v-if="row.signstatus === SignStatus.Signed" type="danger" size="small" @click="showComponent('cancel', row)">解约</el-button>
+                <el-button v-if="[SignStatus.Unsigned, SignStatus.Refuse, SignStatus.Signed].includes(row.signstatus)" type="danger" size="small" @click="showComponent('cancel', row)">修改</el-button>
+            </div>
+        </template>
+    </app-table>
+    <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
+            v-if="componentId" />
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
+import { shallowRef, defineAsyncComponent } from 'vue'
+import { useComponent } from '@/hooks/component'
+import { useComposeTable } from '@pc/components/base/table'
+import { useRequest } from '@/hooks/request'
+import { queryBankAccountSign } from '@/services/api/bank'
+import { getSignStatusName, SignStatus } from '@/constants/bank'
+import { getCertificateTypeCodeName } from '@/constants/account'
 import AppTable from '@pc/components/base/table/index.vue'
 
+const { rowKey, expandKeys, rowClick, selectedRow } = useComposeTable<Model.BankAccountSignRsp>({ rowKey: 'applyexchticket' })
+
+const componentMap = new Map<string, unknown>([
+    ['cancel', defineAsyncComponent(() => import('./components/cancel/index.vue'))],
+])
+
+const { loading, dataList, run } = useRequest(queryBankAccountSign, {})
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
+    run()
+})
+
 const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'accountcode', label: '资金账号' },
+    { prop: 'accountname', label: '账号名' },
+    { prop: 'cardtype', label: '证件类型' },
+    { prop: 'cardno', label: '证件号码' },
+    { prop: 'cusbankname', label: '托管银行' },
+    { prop: 'bankname', label: '签约银行' },
+    { prop: 'bankaccountno', label: '签约银行账号' },
+    { prop: 'currency', label: '币种' },
+    { prop: 'signstatus', label: '状态' },
 ])
+
+const showComponent = (componentName: string, row: Model.BankAccountSignRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
 </script>

+ 1 - 0
src/packages/pc/views/footer/goods/delivery/index.vue

@@ -29,6 +29,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'deliveryinfo', label: '交收信息' },
     { prop: 'orderstatusdisplay', label: '状态' },
     { prop: 'deliveryorderid', label: '交收单号' },
+    { prop: 'matchusername', label: '对手方' },
     { prop: 'reqtime', label: '申请时间' }
 ])
 </script>

+ 2 - 3
src/packages/pc/views/footer/goods/order/cancel/index.vue

@@ -33,11 +33,10 @@ const onCancel = (isRefresh = false) => {
 }
 
 const onCancelSumit = () => {
-    const { wrtradeorderid, buyorsell, marketid} = props.selectedRow
+    const { orderid, marketid} = props.selectedRow
     ///  参数信息
     formData.Header = { MarketID: marketid }
-    formData.OldWRTradeOrderID = wrtradeorderid
-    formData.BuyOrSell = buyorsell
+    formData.OldOrderId = orderid
     /// 提交
     cancelSubmit().then(() => {
         ElMessage.success('提交成功')

+ 3 - 2
src/packages/pc/views/footer/goods/order/index.vue

@@ -16,7 +16,7 @@
         <!-- 展开行 -->
         <template #expand="{ row }">
             <div class="buttonbar">
-                <el-button type="danger" size="small" @click="showComponent('cancel', row)">撤销</el-button>
+                <el-button type="danger" v-if="[3, 7].includes(row.orderstatus)" size="small" @click="showComponent('cancel', row)">撤销</el-button>
             </div>
         </template>
     </app-table>
@@ -44,7 +44,8 @@ defineProps({
 
 const { loading, dataList, run } = useRequest(queryTradeOrderDetail, {
     params: {
-        tradeMode: '50'
+        tradeMode: '50',
+        orderStatus: '3,7',
     },
 })
 

+ 115 - 0
src/packages/pc/views/footer/goods/position/components/delivery/index.vue

@@ -0,0 +1,115 @@
+<!-- 商品订单-合约汇总-交收 -->
+<template>
+    <app-drawer title="交收" width="80%" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="120px" :model="formData" :rules="formRules">
+            <el-form-item label="商品代码/名称">
+                <span>{{ selectedRow.goodscode }}/{{ selectedRow.goodsname }}</span>
+            </el-form-item>
+            <el-form-item label="持仓方向" >
+                <span>{{ getBuyOrSellName(selectedRow.buyorsell) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓金额">
+                <span>{{ formatDecimal(selectedRow.curholderamount) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓数量">
+                <span>{{ formatDecimal(selectedRow.curpositionqty) }}</span>
+            </el-form-item>
+            <el-form-item label="冻结数量">
+                <span>{{ formatDecimal(selectedRow.frozenqty) }}</span>
+            </el-form-item>
+            <el-form-item label="可用数量">
+                <span>{{ formatDecimal(selectedRow.enableqty) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓均价">
+                <span>{{ formatDecimal(selectedRow.averageprice) }}</span>
+            </el-form-item>
+            <el-form-item label="对手方">
+                <span>{{ selectedRow.matchname }}</span>
+            </el-form-item>
+            <el-form-item label="参考损益">
+                <template #value>
+                    <span :class="closepl ? 'g-price-up' : 'g-price-down'">{{ formatDecimal(closepl) }}</span>
+                </template>
+            </el-form-item>
+            <el-form-item prop="DeliveryLot" label="交收数量">
+                <el-input-number placeholder="请输入数量" v-model="formData.DeliveryLot" :max="selectedRow.enableqty" :min="0" />
+            </el-form-item>
+            <el-form-item prop="DeliveryInfo" label="交收信息">
+                <el-input placeholder="请输入交收信息" v-model="formData.DeliveryInfo" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="primary" @click="onSubmit">交收</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, computed } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { useOfflineDelivery } from '@/business/trade'
+import { useFuturesStore } from '@/stores'
+import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
+import { formatDecimal } from '@/filters'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.TradePositionRsp>,
+        required: true,
+    }
+})
+
+const { formSubmit, formData, loading } = useOfflineDelivery()
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getQuoteInfo(props.selectedRow.goodscode)
+
+/// 计算参考损益
+const closepl = computed(() => {
+    const { last = 0 } = quote.value ?? {}
+    const { curpositionqty, curholderamount, agreeunit, buyorsell } = props.selectedRow
+    return (last * curpositionqty * agreeunit - curholderamount) * (buyorsell === BuyOrSell.Buy ? 1 : -1)
+})
+
+// 表单验证规则
+const formRules: FormRules = {
+    DeliveryLot: [{
+        message: '请输入交收数量',
+        validator: () => {
+            return !!formData.DeliveryLot
+        }
+    }],
+    DeliveryInfo: [{
+        required: true,
+        message: '请输入交收信息',
+        validator: () => {
+            return !!formData.DeliveryInfo
+        }
+    }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    const { marketid, goodsid, goodscode, buyorsell } = props.selectedRow
+    /// 市场ID
+    formData.Header = { MarketID: marketid, GoodsID: goodsid }
+    formData.GoodsCode = goodscode
+    formData.GoodsID = goodsid
+    formData.BuyOrSell = buyorsell
+    formSubmit().then(() => {
+        ElMessage.success('交收成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('交收失败:' + err)
+    })
+}
+
+</script>

+ 132 - 0
src/packages/pc/views/footer/goods/position/components/transfer/index.vue

@@ -0,0 +1,132 @@
+<!-- 商品订单-合约汇总-转让 -->
+<template>
+    <app-drawer title="转让" width="80%" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="120px" label-position="left" :model="formData"
+            :rules="formRules">
+            <el-form-item label="商品代码/名称">
+                <span>{{ selectedRow.goodscode }}/{{ selectedRow.goodsname }}</span>
+            </el-form-item>
+            <el-form-item label="持仓方向" >
+                <span>{{ getBuyOrSellName(selectedRow.buyorsell) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓金额">
+                <span>{{ formatDecimal(selectedRow.curholderamount) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓数量">
+                <span>{{ formatDecimal(selectedRow.curpositionqty) }}</span>
+            </el-form-item>
+            <el-form-item label="冻结数量">
+                <span>{{ formatDecimal(selectedRow.frozenqty) }}</span>
+            </el-form-item>
+            <el-form-item label="可用数量">
+                <span>{{ formatDecimal(selectedRow.enableqty) }}</span>
+            </el-form-item>
+            <el-form-item label="持仓均价">
+                <span>{{ formatDecimal(selectedRow.averageprice) }}</span>
+            </el-form-item>
+            <el-form-item label="参考损益">
+                <template #value>
+                    <span :class="closepl ? 'g-price-up' : 'g-price-down'">{{ formatDecimal(closepl) }}</span>
+                </template>
+            </el-form-item>
+            <el-form-item prop="OrderQty" label="转让数量">
+                <el-input-number placeholder="请输入数量" v-model="formData.OrderQty" :max="selectedRow.enableqty" :min="0" />
+            </el-form-item>
+            <el-form-item prop="OrderPrice" label="转让价格">
+                <el-input-number placeholder="请输入价格" v-model="formData.OrderPrice" :decimal-length="selectedRow.decimalplace" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button type="primary" @click="onCloseSumit">提交</el-button>
+            <el-button type="info" @click="onCancel(false)">取消</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, computed, onMounted } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { useOrder } from '@/business/trade'
+import { formatDecimal } from '@/filters'
+import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
+import { useFuturesStore } from '@/stores'
+import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EPriceMode, EValidType } from '@/constants/client'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.TradePositionRsp>,
+        required: true
+    }
+})
+
+const futuresStore = useFuturesStore()
+const quote = futuresStore.getQuoteInfo(props.selectedRow.goodscode)
+// 损益
+const closepl = computed(() => {
+    const { last = 0 } = quote.value ?? {}
+    const { curpositionqty, curholderamount, agreeunit, buyorsell } = props.selectedRow
+    return (last * curpositionqty * agreeunit - curholderamount) * (buyorsell === BuyOrSell.Buy ? 1 : -1)
+})
+
+const { formSubmit, formData, loading } = useOrder()
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+
+const formRules: FormRules = {
+    OrderPrice: [{
+        message: '请输入转让价格',
+        validator: () => {
+            return !!formData.OrderPrice
+        }
+    }],
+    OrderQty: [{
+        message: '请输入转让数量',
+        validator: () => {
+            return !!formData.OrderQty
+        }
+    }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onCloseSumit = () => {
+    const { marketid, goodsid, buyorsell } = props.selectedRow
+        /// 市场ID
+        formData.Header = { GoodsID: goodsid }
+        formData.MarketID = marketid
+        formData.PriceMode = EPriceMode.PRICEMODE_LIMIT
+        formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy
+        formData.GoodsID = goodsid
+        formData.ListingSelectType = EListingSelectType.LISTINGSELECTTYPE_DELISTINGTHENLISTING
+        formData.DelistingType = EDelistingType.DELISTINGTYPE_PRICE
+        formData.BuildType = EBuildType.BUILDTYPE_CLOSE
+        formData.TimevalidType = EValidType.VALIDTYPE_DR
+        formData.OperateType = EOrderOperateType.ORDEROPERATETYPE_NORMAL
+
+        formSubmit().then(() => {
+            ElMessage.success('挂牌成功')
+            onCancel(true)
+        }).catch((err) => {
+            ElMessage.error('挂牌失败:' + err)
+        })
+}
+
+onMounted(() => {
+    const { bid, ask, presettle = 0 } = quote.value ?? {}
+    switch (props.selectedRow.buyorsell) {
+        case BuyOrSell.Buy:
+            formData.OrderPrice = ask || presettle
+            break
+        case BuyOrSell.Sell:
+            formData.OrderPrice = bid || presettle
+            break
+        default:
+            formData.OrderPrice = presettle
+    }
+})
+</script>

+ 33 - 5
src/packages/pc/views/footer/goods/position/index.vue

@@ -5,10 +5,20 @@
         <template #buyorsell="{ value }">
             {{ getBuyOrSellName(value) }}
         </template>
+        <!-- 最新价 -->
+        <template #last="{ row }">
+            {{ last(row) }}
+        </template>
+        <!-- 浮动盈亏-->
+        <template #closepl="{ row }">
+            <span :class="lastColor(row.goodscode)">{{ closepl(row) }}</span>
+        </template>
          <!-- 展开行 -->
          <template #expand>
-            <el-button type="primary" @click="openComponent('delivery')">交收</el-button>
-            <el-button type="primary" @click="openComponent('transfer')">转让</el-button>
+            <div class="buttonbar">
+                <el-button type="primary" size="small" @click="openComponent('delivery')">交收</el-button>
+                <el-button type="danger" size="small" @click="openComponent('transfer')">转让</el-button>
+            </div>
         </template>
     </app-table>
     <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
@@ -18,12 +28,30 @@
 <script lang="ts" setup>
 import { shallowRef, defineAsyncComponent } from 'vue'
 import { useRequest } from '@/hooks/request'
-import { getBuyOrSellName } from '@/constants/order'
+import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
 import { useComponent } from '@/hooks/component'
 import { useComposeTable } from '@pc/components/base/table'
 import { queryTradePosition } from '@/services/api/order'
+import { useFuturesStore } from '@/stores'
 import AppTable from '@pc/components/base/table/index.vue'
 
+const futuresStore = useFuturesStore()
+
+const last = (goodsCode: string) => {
+    return futuresStore.getQuotePrice(goodsCode)
+}
+
+const lastColor = (goodsCode: string) => {
+    return futuresStore.getQuoteInfo(goodsCode).value?.lastColor
+}
+
+/// 计算参考损益
+const closepl = (item: Model.TradePositionRsp) => {
+    const { last = 0 } = futuresStore.getQuoteInfo(item.goodscode).value ?? {}
+    const { curpositionqty, curholderamount, agreeunit, buyorsell } = item
+    return (last * curpositionqty * agreeunit - curholderamount) * (buyorsell === BuyOrSell.Buy ? 1 : -1)
+}
+
 const componentMap = new Map<string, unknown>([
     ['delivery', defineAsyncComponent(() => import('./components/delivery/index.vue'))],
     ['transfer', defineAsyncComponent(() => import('./components/transfer/index.vue'))],
@@ -52,11 +80,11 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'enableqty', label: '可用数量' },
     { prop: 'frozenqty', label: '冻结数量' },
     { prop: 'averageprice', label: '均价' },
-    { prop: 'unknown', label: '现价' },
+    { prop: 'last', label: '现价' },
     { prop: 'curholderamount', label: '持仓金额' },
     { prop: 'usedmargin', label: '占用保证金' },
     { prop: 'marketamount', label: '市值' },
-    { prop: 'unknown', label: '浮动盈亏' },
+    { prop: 'closepl', label: '浮动盈亏' },
 ])
 
 </script>

+ 42 - 5
src/packages/pc/views/footer/performance/buy/index.vue

@@ -1,6 +1,6 @@
 <!-- 资金流水-卖履约-执行中 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading">
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey" :expand-row-keys="expandKeys"  @row-click="rowClick">
          <!-- 履约状态 -->
          <template #performancestatus="{ value }">
             {{ getPerformanceStatusName(value) }}
@@ -17,18 +17,51 @@
         <template #starttime="{ value }">
             {{ formatDate(value) }}
         </template>
+        <!-- 展开行 -->
+        <template #expand="{ row }">
+            <div class="buttonbar">
+                <el-button type="danger" size="small" v-if="row.buyorsell===row.executeside-1" @click="showComponent('manual', row)">手动确认</el-button>
+                <el-button type="warning" size="small" v-if="row.buyorsell===row.executeside-1" @click="showComponent('delay', row)">延期</el-button>
+                <el-button type="primary" size="small" v-if="![6, 7].includes(row.performancestatus)" @click="showComponent('contracted', row)">违约</el-button>
+                <el-button type="info" v-if="![6, 7].includes(row.performancestatus)" size="small" @click="showComponent('edit', row)">修改</el-button>
+                <el-button type="success" size="small" @click="showComponent('details', row)">详细</el-button>
+            </div>
+        </template>
     </app-table>
+    <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
+            v-if="componentId" />
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
+import { shallowRef, defineAsyncComponent} from 'vue'
 import { formatDate } from '@/filters'
 import { useRequest } from '@/hooks/request'
 import { queryPerformancePlan } from '@/services/api/performance'
 import { BuyOrSell, getPaymentTypeName, getPerformanceStatusName, getPerformanceTypeName } from '@/constants/order'
+import { useComposeTable } from '@pc/components/base/table'
+import { useComponent } from '@/hooks/component'
 import AppTable from '@pc/components/base/table/index.vue'
 
-const { loading, dataList } = useRequest(queryPerformancePlan, {
+const componentMap = new Map<string, unknown>([
+    ['manual', defineAsyncComponent(() => import('../components/manual/index.vue'))],
+    ['details', defineAsyncComponent(() => import('../components/details/index.vue'))],
+    ['contracted', defineAsyncComponent(() => import('../components/contracted/index.vue'))],
+    ['delay', defineAsyncComponent(() => import('../components/delay/index.vue'))],
+    ['edit', defineAsyncComponent(() => import('../components/edit/index.vue'))],
+])
+
+defineProps({
+    code: String
+})
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
+    run()
+})
+
+const { rowKey, expandKeys, rowClick } = useComposeTable<Model.PerformancePlanRsp>({ rowKey: 'performanceplanid' })
+const selectedRow = shallowRef<Model.PerformancePlanRsp>()
+
+const { loading, dataList, run } = useRequest(queryPerformancePlan, {
     params: {
         buyorsell: BuyOrSell.Buy,
         status:'2'
@@ -49,7 +82,11 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'buyusername', label: '买方' },
     { prop: 'performancestatus', label: '履约状态' },
     { prop: 'curstepname', label: '当前步骤' },
-    { prop: 'starttime', label: '开始时间' },
-    
+    { prop: 'starttime', label: '开始时间' }
 ])
+
+const showComponent = (componentName: string, row: Model.PerformancePlanRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
 </script>

+ 81 - 0
src/packages/pc/views/footer/performance/components/contracted/index.vue

@@ -0,0 +1,81 @@
+<!-- 订单管理-我的履约-违约申请 -->
+<template>
+    <app-drawer title="违约申请" :width="480" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" label-width="80px" :model="formData" :rules="formRules">
+            <el-form-item label="关联单号">
+                <span>{{ handleNoneValue(selectedRow.relatedorderid) }}</span>
+            </el-form-item>
+            <el-form-item label="当前步骤">
+                <span>{{ handleNumberValue(getPerformanceStepStatusName(selectedRow.steptypeid)) }}</span>
+            </el-form-item>
+            <el-form-item label="备注" prop="ApplyRemark">
+                <el-input type="textarea" :rows="3" v-model="formData.ApplyRemark" />
+            </el-form-item>
+            <el-form-item label="附件" prop="Attachment">
+                <app-upload :file-types="['image']" type-message="请选择正确的图片类型" @change="onUploadChange" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="primary" @click="onSubmit">确认</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { handleNoneValue, handleNumberValue } from '@/filters'
+import { getPerformanceStepStatusName } from '@/constants/order'
+import { usePerformanceContractedApply } from '@/business/performance'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppUpload from '@pc/components/base/upload/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.PerformancePlanRsp>,
+        default: () => ({})
+    }
+})
+
+const { loading, formData, formSubmit } = usePerformanceContractedApply(props.selectedRow.buyaccountid)
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+
+const formRules: FormRules = {
+    Attachment: [{
+        required: true,
+        validator: (rule, value, callback) => {
+            if (formData.Attachment) {
+                callback()
+            } else {
+                callback(new Error('请上传附件'))
+            }
+        }
+    }],
+}
+
+const onUploadChange = (file: { filePath: string }) => {
+    formData.Attachment = file.filePath
+    formRef.value?.validateField('Attachment')
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            formSubmit().then(() => {
+                ElMessage.success('提交成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('提交失败:' + err)
+            })
+        }
+    })
+}
+</script>

+ 68 - 0
src/packages/pc/views/footer/performance/components/delay/index.vue

@@ -0,0 +1,68 @@
+<!-- 订单管理-我的履约-延期申请 -->
+<template>
+    <app-drawer title="延期申请" :width="480" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" label-width="110px" :model="formData" :rules="formRules">
+            <el-form-item label="关联单号">
+                <span>{{ handleNoneValue(selectedRow.relatedorderid) }}</span>
+            </el-form-item>
+            <el-form-item label="当前步骤">
+                <span>{{ handleNumberValue(getPerformanceStepStatusName(selectedRow.steptypeid)) }}</span>
+            </el-form-item>
+            <el-form-item label="申请延期天数" prop="delaydays">
+                <el-input type="number" placeholder="请输入" v-model="formData.delaydays" />
+            </el-form-item>
+            <el-form-item label="备注" prop="applyremark">
+                <el-input type="textarea" :rows="3" v-model="formData.applyremark" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="primary" @click="onSubmit">确认</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { handleNoneValue, handleNumberValue } from '@/filters'
+import { getPerformanceStepStatusName } from '@/constants/order'
+import { usePerformanceDelayApply } from '@/business/performance'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.PerformancePlanRsp>,
+        default: () => ({})
+    }
+})
+
+const { loading, formData, formSubmit } = usePerformanceDelayApply()
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+
+const formRules: FormRules = {
+    delaydays: [{ required: true, message: '请输入延期天数', trigger: 'blur' }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    /// 当前步骤ID
+    formData.value.PerformancePlanStepID = props.selectedRow.curstepid
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            formSubmit().then(() => {
+                ElMessage.success('提交成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('提交失败:' + err)
+            })
+        }
+    })
+}
+</script>

+ 50 - 0
src/packages/pc/views/footer/performance/components/details/index.less

@@ -0,0 +1,50 @@
+.trade-details {
+    padding: 20px;
+
+    .card-header {
+        display: flex;
+        align-items: center;
+        font-size: 16px;
+        color: #999;
+        cursor: pointer;
+
+        h4 {
+            margin-left: 10px;
+        }
+    }
+
+    .el-aside {
+        min-width: 400px;
+        width: auto;
+
+        .el-card {
+            ul {
+                li {
+                    display: flex;
+                    justify-content: space-between;
+
+                    &:not(:first-child) {
+                        margin-top: 20px;
+                    }
+
+                    >span {
+                        &:first-child {
+                            width: 120px;
+                            color: #94A0AF;
+                        }
+
+                        &:last-child {
+                            flex: 1;
+                            text-align: right;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    .el-main {
+        margin-left: 20px;
+        padding: 0;
+    }
+}

+ 210 - 0
src/packages/pc/views/footer/performance/components/details/index.vue

@@ -0,0 +1,210 @@
+<!-- 履约信息-详情 -->
+<template>
+    <app-drawer class="g-details" width="80%" title="详情" v-model:show="show" :loading="loading" :refresh="refresh">
+        <app-table-details title="基本信息" :label-width="180" :data="selectedRow" :cell-props="detailProps" :column="2">
+            <!-- 付款方式 -->
+            <template #paymenttype="{ value }">
+                {{ value === 1 ? '冻结' : '扣款' }}
+            </template>
+            <!-- 买方冻结 -->
+            <template #buyerfreezeamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 卖方冻结 -->
+            <template #sellerfreezeamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 买方冻结剩余 -->
+            <template #buyerfreezeamountremain="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 卖方冻结剩余 -->
+            <template #sellerfreezeamountremain="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 买方今日付款 -->
+            <template #buytodayamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 卖方今日收款 -->
+            <template #selltodayamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 已付金额 -->
+            <template #buypaidamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 已收金额 -->
+            <template #sellreceivedamount="{ value }">
+                {{ handleNumberValue(value) }}
+            </template>
+            <!-- 买方联络信息 -->
+            <template #buyerInfo>
+                {{ buyerInfo }}
+            </template>
+            <!-- 卖方联络信息 -->
+            <template #sellerInfo>
+                {{ sellerInfo }}
+            </template>
+        </app-table-details>
+        <app-table :data="dataList" :columns="tableColumns" :show-toolbar="false" :row-style="rowStyle" border>
+            <template #header>
+                <h3 class="g-details__title">步骤列表</h3>
+            </template> 
+            <!-- 步骤值 -->
+            <template #stepvalue="{ value }">
+                {{ (value * 100).toFixed(1) }}
+            </template>
+            <template #isauto="{ value }">
+                {{ value ? '是' : '否' }}
+            </template>
+            <!-- 启动类型 -->
+            <template #steplanchtype="{ value }">
+                {{ value === 1 ? '系统自动' : '手动' }}
+            </template>
+            <!-- 步骤状态 -->
+            <template #stepstatus="{ value }">
+                {{ getPerformanceStepStatusName(value) }}
+            </template>
+        </app-table> 
+        <template #footer>
+            <el-button @click="onCancel(false)" plain>取消</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+export default defineComponent({
+    inheritAttrs: false,
+})
+</script>
+
+<script lang="ts" setup>
+import { PropType, computed, ref, shallowRef } from 'vue'
+import { handleNumberValue, handleNoneValue } from '@/filters'
+import { getPerformanceStepStatusName } from '@/constants/order'
+import { useRequest } from '@/hooks/request'
+import { queryWrPerformancePlanStep } from '@/services/api/performance'
+import AppTable from '@pc/components/base/table/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    code: String,
+    teleportTo: {
+        type: String,
+        default: '#appPageTeleport'
+    },
+    selectedRow: {
+        type: Object as PropType<Model.PerformancePlanRsp>,
+        default: () => ({})
+    }
+})
+
+const show = ref(true)
+const refresh = ref(false)
+
+const { loading, dataList } = useRequest(queryWrPerformancePlanStep, {
+    params: {
+        planid: props.selectedRow.performanceplanid
+    },
+})
+
+const detailProps = [
+    { prop: 'relatedorderid', label: '关联单号:' },
+    { prop: 'wrstandardname', label: '履约商品:' },
+    { prop: 'amount', label: '履约金额:' },
+    { prop: 'paymenttype', label: '付款方式:' },
+    { prop: 'accountname', label: '对手方:' },
+    { prop: 'buyerfreezeamount', label: '买方冻结:' },
+    { prop: 'buytodayamount', label: '买方今日付款:' },
+    { prop: 'sellerfreezeamount', label: '卖方冻结:' },
+    { prop: 'selltodayamount', label: '卖方今日收款:' },
+    { prop: 'buyerfreezeamountremain', label: '买方冻结剩余:' },
+    { prop: 'buypaidamount', label: '已付金额:' },
+    { prop: 'sellerfreezeamountremain', label: '卖方冻结剩余:' },
+    { prop: 'sellreceivedamount', label: '已收金额:' },
+    { prop: 'sellerInfo', label: '卖方联络信息:' },
+    { prop: 'buyerInfo', label: '买方联络信息:' },
+]
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { prop: 'steptypename', label: '名称' },
+    { prop: 'stepdays', label: '天数' },
+    { prop: 'remaindays', label: '剩余天数' },
+    { prop: 'stepvalue', label: '步骤值(%)' },
+    { prop: 'stepamount', label: '金额' },
+    { prop: 'realamount', label: '完成金额' },
+    { prop: 'isauto', label: '是否自动' },
+    { prop: 'steplanchtype', label: '启动类型' },
+    { prop: 'starttime', label: '开始日期' },
+    { prop: 'endtime', label: '结束日期' },
+    { prop: 'stepstatus', label: '步骤状态' },
+    { prop: 'remark', label: '步骤备注' },
+])
+
+// 当前步骤索引位置
+const currentStepIndex = computed(() => dataList.value.findIndex((e) => e.performancestepid === props.selectedRow.curstepid))
+
+// 买方联络信息
+const buyerInfo = computed(() => {
+    if (props.selectedRow.buyerinfo) {
+        const obj = JSON.parse(props.selectedRow.buyerinfo)
+        const content: string[] = []
+        Object.entries(obj).forEach(([key, value]) => {
+            if (value) {
+                switch (key) {
+                    case 'ContactInfo': {
+                        content.push('联络信息:' + value)
+                        break
+                    }
+                    case 'ReceiveInfo': {
+                        content.push('收货地址:' + value)
+                        break
+                    }
+                    case 'ReceiptInfo': {
+                        content.push('发票信息:' + value)
+                        break
+                    }
+                }
+            }
+        })
+        return content.join('\n')
+    }
+    return handleNoneValue()
+})
+
+// 卖方联络信息
+const sellerInfo = computed(() => {
+    const obj = JSON.parse(props.selectedRow.sellerinfo || '{}')
+    return obj.ContactInfo || handleNoneValue()
+})
+
+const rowStyle = ({ rowIndex }: { rowIndex: number }) => {
+    if (currentStepIndex.value > rowIndex) {
+        return {
+            color: 'green'
+        }
+    }
+    if (currentStepIndex.value === rowIndex) {
+        return {
+            color: 'red'
+        }
+    }
+    return {
+        color: '#999'
+    }
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+// getPlanStepList()
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 182 - 0
src/packages/pc/views/footer/performance/components/edit/index.vue

@@ -0,0 +1,182 @@
+<!-- 订单管理-我的履约-修改联络信息 -->
+<template>
+    <app-drawer title="修改联络信息" width="80%" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="80px" :model="formData" :rules="formRules">
+            <el-form-item label="履约计划">
+                <span>{{ handleNoneValue(selectedRow.curstepid) }}</span>
+            </el-form-item>
+            <el-form-item label="联络信息">
+                <el-input type="textarea" :rows="3" v-model="Contract" />
+            </el-form-item>
+            <template v-if="code === 'order_buy_edit'">
+                <el-form-item label="收货地址">
+                    <div class="el-form-item--col">
+                        <el-input type="textarea" :rows="2" v-model="Receive" />
+                        <el-dropdown trigger="click" v-if="addressList.length">
+                            <el-icon :size="20" style="cursor: pointer;">
+                                <CirclePlusFilled />
+                            </el-icon>
+                            <template #dropdown>
+                                <el-dropdown-menu>
+                                    <el-dropdown-item v-for="(item, index) in addressList" :key="index"
+                                        @click="addressChange(item)">
+                                        {{ [item.provincename, item.cityname, item.districtname, item.address].join(' ') }}
+                                    </el-dropdown-item>
+                                </el-dropdown-menu>
+                            </template>
+                        </el-dropdown>
+                    </div>
+                </el-form-item>
+                <el-form-item label="发票信息">
+                    <div class="el-form-item--col">
+                        <el-input type="textarea" :rows="2" v-model="Receipt" />
+                        <el-dropdown trigger="click" v-if="invoiceList.length">
+                            <el-icon :size="20" style="cursor: pointer;">
+                                <CirclePlusFilled />
+                            </el-icon>
+                            <template #dropdown>
+                                <el-dropdown-menu>
+                                    <el-dropdown-item v-for="(item, index) in invoiceList" :key="index"
+                                        @click="invoiceChange(item)">
+                                        {{ [getReceiptTypeName(item.receipttype), item.username].join(' ') }}
+                                    </el-dropdown-item>
+                                </el-dropdown-menu>
+                            </template>
+                        </el-dropdown>
+                    </div>
+                </el-form-item>
+            </template>
+        </el-form>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="primary" @click="onSubmit">确认</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, onMounted, shallowRef } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { handleNoneValue } from '@/filters'
+import { getReceiptTypeName } from '@/constants/receipt'
+import { useRequest } from '@/hooks/request'
+import { queryUserReceiveInfo, queryWrUserReceiptInfo } from '@/services/api/user'
+import { usePerformanceModifyContact } from '@/business/performance'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    code: String,
+    selectedRow: {
+        type: Object as PropType<Model.PerformancePlanRsp>,
+        required: true
+    }
+})
+
+const { loading, formData, formSubmit } = usePerformanceModifyContact()
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+const formRules: FormRules = {}
+
+const Contract = shallowRef('') // 联系人嘻嘻
+const Receive = shallowRef('') // 收货地址信息
+const Receipt = shallowRef('') // 发票信息
+
+const { dataList: addressList } = useRequest(queryUserReceiveInfo)
+const { dataList: invoiceList } = useRequest(queryWrUserReceiptInfo)
+
+// 选择地址
+const addressChange = (item: Model.UserReceiveInfoRsp) => {
+    Receive.value = [item.provincename, item.cityname, item.districtname, item.address].join(' ')
+}
+
+// 选择发票
+const invoiceChange = (item: Model.WrUserReceiptInfoRsp) => {
+    Receipt.value = ''
+    Object.entries(item).forEach(([key, value]) => {
+        if (value !== '') {
+            switch (key) {
+                case 'receipttype': {
+                    Receipt.value += '发票类型:' + getReceiptTypeName(Number(value)) + '\n'
+                    break
+                }
+                case 'username': {
+                    Receipt.value += '户名:' + value + '\n'
+                    break
+                }
+                case 'address': {
+                    Receipt.value += '地址:' + value + '\n'
+                    break
+                }
+                case 'contactinfo': {
+                    Receipt.value += '联系方式:' + value + '\n'
+                    break
+                }
+                case 'idnum': {
+                    Receipt.value += '身份证号码:' + value + '\n'
+                    break
+                }
+                case 'receiptaccount': {
+                    Receipt.value += '发票帐号:' + value + '\n'
+                    break
+                }
+                case 'receiptbank': {
+                    Receipt.value += '发票开户行:' + value + '\n'
+                    break
+                }
+                case 'taxpayerid': {
+                    Receipt.value += '纳税人识别号:' + value + '\n'
+                    break
+                }
+            }
+        }
+    })
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    /// PerformancePlanID
+    formData.value.PerformancePlanID = props.selectedRow.performanceplanid
+    /// Json String
+    const json =  {
+        ContactInfo: `${Contract.value}`,
+        ReceiveInfo: `${Receive.value}`,
+        ReceiptInfo: `${Receipt.value}`
+    }
+    formData.value.ContactInfo= JSON.stringify(json)
+
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            formSubmit().then(() => {
+                ElMessage.success('提交成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('提交失败:' + err)
+            })
+        }
+    })
+}
+
+onMounted(() => {
+    switch (props.code) {
+        case 'order_buy_edit':
+            if (props.selectedRow.buyerinfo) {
+                const res = JSON.parse(props.selectedRow.buyerinfo)
+                Contract.value = res.ContactInfo
+                Receive.value = res.ReceiveInfo
+                Receipt .value= res.ReceiptInfo
+            }
+            break
+        case 'order_sell_edit':
+            if (props.selectedRow.sellerinfo) {
+                const res = JSON.parse(props.selectedRow.sellerinfo)
+                Contract.value = res.ContactInfo
+            }
+            break
+    }
+})
+</script>

+ 61 - 0
src/packages/pc/views/footer/performance/components/manual/index.vue

@@ -0,0 +1,61 @@
+<!-- 履约信息-手动执行 -->
+<template>
+    <app-drawer title="手动执行" :width="480" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" label-width="60px" :model="confirmFormData" :rules="formRules">
+            <el-form-item>
+                <span>是否执行当前步骤【{{ handleNumberValue(getPerformanceStepStatusName(selectedRow.steptypeid)) }}】?</span>
+            </el-form-item>
+            <el-form-item label="备注" prop="StepRemark">
+                <el-input type="textarea" :rows="3" v-model="confirmFormData.StepRemark" />
+            </el-form-item>
+        </el-form>
+        <template #footer>
+            <el-button @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="primary" @click="onSubmit">确认</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { handleNumberValue } from '@/filters'
+import { getPerformanceStepStatusName } from '@/constants/order'
+import { usePerformanceManualConfirm } from '@/business/performance'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.PerformancePlanRsp>,
+        default: () => ({})
+    }
+})
+
+const { loading, confirmSubmit, confirmFormData } = usePerformanceManualConfirm(props.selectedRow.buyaccountid)
+const show = ref(true)
+const refresh = ref(false)
+const formRef = ref<FormInstance>()
+
+const formRules: FormRules = {
+    StepRemark: [{ required: true, message: '请输入备注', trigger: 'blur' }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    confirmFormData.value.PerformancePlanStepID = props.selectedRow.performanceplanid
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            confirmSubmit().then(() => {
+                ElMessage.success('提交成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('提交失败:' + err)
+            })
+        }
+    })
+}
+</script>

+ 41 - 3
src/packages/pc/views/footer/performance/sell/index.vue

@@ -1,6 +1,6 @@
 <!-- 底部-履约信息-卖履约 -->
 <template>
-    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading">
+    <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading" :row-key="rowKey" :expand-row-keys="expandKeys"  @row-click="rowClick">
          <!-- 履约状态 -->
          <template #performancestatus="{ value }">
             {{ getPerformanceStatusName(value) }}
@@ -17,18 +17,51 @@
         <template #starttime="{ value }">
             {{ formatDate(value) }}
         </template>
+        <!-- 展开行 -->
+        <template #expand="{ row }">
+            <div class="buttonbar">
+                <el-button type="danger" size="small" v-if="row.buyorsell===row.executeside-1" @click="showComponent('manual', row)">手动确认</el-button>
+                <el-button type="warning" size="small" v-if="row.buyorsell===row.executeside-1" @click="showComponent('delay', row)">延期</el-button>
+                <el-button type="primary" size="small" v-if="![6, 7].includes(row.performancestatus)" @click="showComponent('contracted', row)">违约</el-button>
+                <el-button type="info" v-if="![6, 7].includes(row.performancestatus)" size="small" @click="showComponent('edit', row)">修改</el-button>
+                <el-button type="success" size="small" @click="showComponent('details', row)">详细</el-button>
+            </div>
+        </template>
     </app-table>
+    <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)" @closed="closeComponent"
+            v-if="componentId" />
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
+import { shallowRef, defineAsyncComponent } from 'vue'
 import { formatDate } from '@/filters'
 import { useRequest } from '@/hooks/request'
 import { queryPerformancePlan } from '@/services/api/performance'
 import { BuyOrSell, getPaymentTypeName, getPerformanceStatusName, getPerformanceTypeName } from '@/constants/order'
+import { useComposeTable } from '@pc/components/base/table'
+import { useComponent } from '@/hooks/component'
 import AppTable from '@pc/components/base/table/index.vue'
 
-const { loading, dataList } = useRequest(queryPerformancePlan, {
+const componentMap = new Map<string, unknown>([
+    ['manual', defineAsyncComponent(() => import('../components/manual/index.vue'))],
+    ['details', defineAsyncComponent(() => import('../components/details/index.vue'))],
+    ['contracted', defineAsyncComponent(() => import('../components/contracted/index.vue'))],
+    ['delay', defineAsyncComponent(() => import('../components/delay/index.vue'))],
+    ['edit', defineAsyncComponent(() => import('../components/edit/index.vue'))],
+])
+
+defineProps({
+    code: String
+})
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
+    run()
+})
+
+const { rowKey, expandKeys, rowClick } = useComposeTable<Model.PerformancePlanRsp>({ rowKey: 'performanceplanid' })
+const selectedRow = shallowRef<Model.PerformancePlanRsp>()
+
+const { loading, dataList, run } = useRequest(queryPerformancePlan, {
     params: {
         buyorsell: BuyOrSell.Sell,
         status:'2'
@@ -52,4 +85,9 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { prop: 'starttime', label: '开始时间' },
     
 ])
+
+const showComponent = (componentName: string, row: Model.PerformancePlanRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
 </script>

+ 3 - 2
src/packages/pc/views/footer/spot/order/index.vue

@@ -20,7 +20,7 @@
         <!-- 展开行 -->
         <template #expand="{ row }">
             <div class="buttonbar">
-                <el-button type="danger" size="small" @click="showComponent('cancel', row)">撤销</el-button>
+                <el-button type="danger" v-if="[3, 7].includes(row.wrtradeorderstatus)" size="small" @click="showComponent('cancel', row)">撤销</el-button>
             </div>
         </template>
     </app-table>
@@ -50,7 +50,8 @@ const { loading, dataList, run } = useRequest(queryWrOrderDetail, {
     params: {
         wrtradetype: 1,
         marketid: 17201,
-        haswr: 1
+        haswr: 1,
+        bcancel: 1
     },
 })
 

+ 8 - 8
src/packages/pc/views/footer/spot/position/components/listing/index.vue

@@ -4,22 +4,22 @@
         <el-form ref="formRef" class="el-form--vertical" label-width="120px" label-position="left" :model="formData"
             :rules="formRules">
             <el-form-item label="商品代码/名称">
-                <span>{{selectedRow.wrstandardcode}}/{{ selectedRow.wrstandardname }}</span>
+                <span>{{ selectedRow.wrstandardcode }}/{{ selectedRow.wrstandardname }}</span>
             </el-form-item>
             <el-form-item label="仓库" >
-                <span>{{selectedRow.warehousename}}</span>
+                <span>{{ selectedRow.warehousename }}</span>
             </el-form-item>
             <el-form-item label="库存量">
-                <span>{{selectedRow.qty}}</span>
+                <span>{{ selectedRow.qty }}</span>
             </el-form-item>
             <el-form-item label="冻结量">
-                <span>{{selectedRow.freezerqty}}</span>
+                <span>{{ selectedRow.freezerqty }}</span>
             </el-form-item>
             <el-form-item label="可用量">
-                <span>{{selectedRow.enableqty}}</span>
+                <span>{{ selectedRow.enableqty }}</span>
             </el-form-item>
             <el-form-item prop="OrderQty" label="挂牌数量">
-                <el-input-number placeholder="请输入挂牌数量" v-model="formData.OrderQty" :max="selectedRow.enableqty" min="0" />
+                <el-input-number placeholder="请输入挂牌数量" v-model="formData.OrderQty" :max="selectedRow.enableqty" :min="0" />
             </el-form-item>
             <el-form-item prop="FixedPrice" label="挂牌价格">
                 <el-input-number placeholder="请输入挂牌价格" v-model="formData.FixedPrice" />
@@ -42,10 +42,10 @@ const props = defineProps({
     selectedRow: {
         type: Object as PropType<Model.HoldLBRsp>,
         required: true
-    }
+    },
+    code: String
 })
 
-console.log(props.selectedRow)
 
 const { formData, listingSubmit, loading } = useHdWROrder()
 const show = ref(true)

+ 174 - 31
src/packages/pc/views/footer/spot/position/components/pickup/index.vue

@@ -1,48 +1,198 @@
 <!-- 现货仓单-现货明细-提货 -->
 <template>
-    <app-drawer title="充值" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" class="el-form--vertical" label-width="100px" label-position="left" :model="formData"
+    <app-drawer title="提货" v-model:show="show" :width="960" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="120px" :model="formData"
             :rules="formRules">
-            <el-form-item prop="Amount" label="充值金额">
-                <el-input-number placeholder="请输入" v-model="formData.Amount" />
+            <el-form-item label="商品代码/名称">
+                <span>{{selectedRow.wrstandardcode}}/{{ selectedRow.wrstandardname }}</span>
             </el-form-item>
-            <el-form-item label="凭证">
-                <app-upload :file-types="['image']" type-message="请选择正确的图片类型" @change="onUploadChange" />
+            <el-form-item label="仓库" >
+                <span>{{selectedRow.warehousename}}</span>
+            </el-form-item>
+            <el-form-item label="库存量">
+                <span>{{selectedRow.qty}}</span>
+            </el-form-item>
+            <el-form-item label="冻结量">
+                <span>{{selectedRow.freezerqty}}</span>
+            </el-form-item>
+            <el-form-item label="可用量">
+                <span>{{selectedRow.enableqty}}</span>
+            </el-form-item>
+            <el-form-item prop="OrderQty" label="提货数量">
+                <el-input-number placeholder="请输入提货数量" v-model="orderQty" :max="selectedRow.enableqty" :min="0" />
+            </el-form-item>
+            <el-form-item prop="AppointmentModel" label="提货方式">
+                <el-select v-model="formData.AppointmentModel" :disabled="loading">
+                    <el-option :label="item.label" :value="item.value" v-for="(item, index) in getAppointmentModelOutList()"
+                        :key="index" />
+                </el-select>
+            </el-form-item>
+            <el-form-item prop="ContractName" label="联系人">
+                <div class="el-form-item--col">
+                        <el-input type="textarea" :rows="2" v-model="formData.ContactName" />
+                        <el-dropdown trigger="click" v-if="addressList.length">
+                            <el-icon :size="20" style="cursor: pointer;">
+                                <CirclePlusFilled />
+                            </el-icon>
+                            <template #dropdown>
+                                <el-dropdown-menu>
+                                    <el-dropdown-item v-for="(item, index) in addressList" :key="index"
+                                        @click="addressChange(item)">
+                                        {{ [item.provincename, item.cityname, item.districtname, item.address].join(' ') }}
+                                    </el-dropdown-item>
+                                </el-dropdown-menu>
+                            </template>
+                        </el-dropdown>
+                    </div>
+            </el-form-item>
+            <el-form-item prop="ContactNum" label="联系方式">
+                <el-input placeholder="请输入" v-model="formData.ContactNum" />
+            </el-form-item>
+            <el-form-item prop="Region" class="el-form-item--row" label="收货地区">
+                <app-region class="el-form-item--col" v-model:province="formData.ProvinceID"
+                    v-model:city="formData.CityID" v-model:district="formData.DistrictID" />
+            </el-form-item>
+            <el-form-item class="el-form-item--row" prop="Address" label="收货地址">
+                <el-input placeholder="请输入" v-model="formData.Address" />
+            </el-form-item>
+            <el-form-item prop="AppointmentRemark" class="el-form-item--row" label="发票信息">
+                <div class="el-form-item--col">
+                    <el-input type="textarea" :rows="2" v-model="formData.AppointmentRemark" />
+                    <el-dropdown trigger="click" v-if="invoiceList.length">
+                        <el-icon :size="20" style="cursor: pointer;">
+                            <CirclePlusFilled />
+                        </el-icon>
+                        <template #dropdown>
+                            <el-dropdown-menu>
+                                <el-dropdown-item v-for="(item, index) in invoiceList" :key="index"
+                                    @click="invoiceChange(item)">
+                                    {{ [getReceiptTypeName(item.receipttype), item.username].join(' ') }}
+                                </el-dropdown-item>
+                            </el-dropdown-menu>
+                        </template>
+                    </el-dropdown>
+                </div>
             </el-form-item>
         </el-form>
         <template #footer>
-            <el-button type="primary" @click="formSubmit">提交</el-button>
+            <el-button type="primary" @click="onSubmit">提货</el-button>
             <el-button @click="onCancel(false)" plain>取消</el-button>
         </template>
     </app-drawer>
 </template>
 
 <script lang="ts" setup>
-import { ref, PropType } from 'vue'
+import { shallowRef, ref, PropType } from 'vue'
 import { ElMessage, FormInstance, FormRules } from 'element-plus'
-import { useDoDeposit } from '@/business/bank'
+import { useWrOutInApply } from '@/business/trade'
+import { useRequest } from '@/hooks/request'
+import { getAppointmentModelOutList } from '@/constants/order'
+import { queryUserReceiveInfo, queryWrUserReceiptInfo } from '@/services/api/user'
+import { getReceiptTypeName } from '@/constants/receipt'
+import AppRegion from '@pc/components/base/region/index.vue'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
-import AppUpload from '@pc/components/base/upload/index.vue'
+
+const { dataList: invoiceList } = useRequest(queryWrUserReceiptInfo)
+const { dataList: addressList } = useRequest(queryUserReceiveInfo)
 
 const props = defineProps({
     selectedRow: {
-        type: Object as PropType<Model.TaAccountsRsp>,
-        required: true
+        type: Object as PropType<Model.HoldLBRsp>,
+        required: true,
     }
 })
 
-const { formData, onSubmit, extendInfo, loading } = useDoDeposit(props.selectedRow.userid)
+const { formData, applySubmit, orderQty, loading } = useWrOutInApply(props.selectedRow)
 const show = ref(true)
 const refresh = ref(false)
 const formRef = ref<FormInstance>()
+const regionName = shallowRef('') // 地区名称
 
+// 表单验证规则
 const formRules: FormRules = {
-    Amount: [{ required: true, message: '请输入充值金额' }],
+    orderQty: [{
+        message: '请输入提货数量',
+        validator: () => {
+            return !!orderQty.value
+        }
+    }],
+    ContactName: [{
+        required: true,
+        message: '请输入联系人',
+    }],
+    ContactNum: [{
+        required: true,
+        message: '请输入联系方式',
+    }],
+    Region: [{
+        message: '请选择收货地区',
+        validator: () => {
+            return !!formData.ProvinceID && !!formData.CityID && !!formData.DistrictID
+        }
+    }],
+    Address: [{
+        required: true,
+        message: '请输入收货地址',
+    }],
+    AppointmentRemark: [{
+        required: true,
+        message: '请输入发票信息',
+    }],
 }
 
-const onUploadChange = (file: { filePath: string }) => {
-    console.log(file.filePath)
-    extendInfo.certificate_photo_url = file.filePath
+// 选择地址
+const addressChange = (item: Model.UserReceiveInfoRsp) => {
+    formData.ContactName = item.receivername
+    formData.ContactNum = item.phonenum
+    formData.ProvinceID = item.provinceid
+    formData.CityID = item.cityid
+    formData.DistrictID = item.districtid
+    formData.Address = item.address
+    regionName.value = [item.provincename, item.cityname, item.districtname].join(' ')
+}
+
+
+// 选择发票
+const invoiceChange = (item: Model.WrUserReceiptInfoRsp) => {
+    formData.AppointmentRemark = ''
+    Object.entries(item).forEach(([key, value]) => {
+        if (value !== '') {
+            switch (key) {
+                case 'receipttype': {
+                    formData.AppointmentRemark += '发票类型:' + getReceiptTypeName(Number(value)) + '\n'
+                    break
+                }
+                case 'username': {
+                    formData.AppointmentRemark += '户名:' + value + '\n'
+                    break
+                }
+                case 'address': {
+                    formData.AppointmentRemark += '地址:' + value + '\n'
+                    break
+                }
+                case 'contactinfo': {
+                    formData.AppointmentRemark += '联系方式:' + value + '\n'
+                    break
+                }
+                case 'idnum': {
+                    formData.AppointmentRemark += '身份证号码:' + value + '\n'
+                    break
+                }
+                case 'receiptaccount': {
+                    formData.AppointmentRemark += '发票帐号:' + value + '\n'
+                    break
+                }
+                case 'receiptbank': {
+                    formData.AppointmentRemark += '发票开户行:' + value + '\n'
+                    break
+                }
+                case 'taxpayerid': {
+                    formData.AppointmentRemark += '纳税人识别号:' + value + '\n'
+                    break
+                }
+            }
+        }
+    })
 }
 
 const onCancel = (isRefresh = false) => {
@@ -50,20 +200,13 @@ const onCancel = (isRefresh = false) => {
     refresh.value = isRefresh
 }
 
-const formSubmit = () => {
-    formRef.value?.validate((valid) => {
-        if (valid) {
-            if (formData.CusBankID) {
-                onSubmit().then(() => {
-                    ElMessage.success('提交成功')
-                    onCancel(true)
-                }).catch((err) => {
-                    ElMessage.error('提交失败:' + err)
-                })
-            } else {
-                ElMessage.error('未签约')
-            }
-        }
+const onSubmit = () => {
+    applySubmit().then(() => {
+        ElMessage.success('提货申请成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('提货申请失败:' + err)
     })
 }
+
 </script>

+ 21 - 0
src/packages/pc/views/market/trade/goods/detail/components/chart/index.less

@@ -0,0 +1,21 @@
+.market-trade-goods-detail-chart {
+    display: flex;
+    height: 100%;
+
+    >.block-left {
+        flex: 1;
+    }
+
+    >.block-right {
+        display: flex;
+        flex-direction: column;
+        width: 240px;
+        background-color: #14181B;
+        border-left: 1px solid #33393D;
+
+        .app-quote-tik {
+            flex: 1;
+            overflow-y: auto;
+        }
+    }
+}

+ 6 - 2
src/packages/pc/views/market/trade/goods/detail/components/chart/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="market-trade-goods-detail__container">
+    <div class="market-trade-goods-detail-chart">
         <div class="block-left">
             <Chart v-bind="{ goodsCode }" @ready="onReady" />
         </div>
@@ -33,4 +33,8 @@ const onReady = (start: string, end: string) => {
     startTime.value = formatDate(start)
     endTime.value = formatDate(end)
 }
-</script>
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 2
src/packages/pc/views/market/trade/goods/detail/components/order/delisting/index.vue

@@ -1,8 +1,7 @@
 <!-- 交易市场-订单交易-买卖大厅-摘牌 -->
 <template>
     <app-drawer title="摘牌" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" class="el-form--vertical" label-width="100px" label-position="left" :model="formData"
-            :rules="formRules">
+        <el-form ref="formRef" class="el-form--vertical" label-width="100px" :model="formData" :rules="formRules">
             <el-form-item label="挂牌方">
                 <span>{{ selectedRow.username }}</span>
             </el-form-item>

+ 1 - 0
src/packages/pc/views/market/trade/goods/detail/components/order/index.less

@@ -1,6 +1,7 @@
 .market-trade-goods-detail-order {
     display: flex;
     height: 100%;
+    background-color: #181e22;
 
     .app-table {
         flex: 1;

+ 0 - 41
src/packages/pc/views/market/trade/goods/detail/index.less

@@ -1,41 +0,0 @@
-.g-view-detail {
-    &__header {
-        display: flex;
-        align-items: center;
-        gap: 10px;
-        width: 100%;
-        background-color: #181e22;
-        border-bottom: 1px solid #3a87f7;
-        padding: 4px;
-
-        .breadcrumb {
-            color: #0D96FF;
-        }
-
-        .btnbar {
-            margin-left: auto;
-        }
-    }
-
-    &__container {
-        display: flex;
-        height: 100%;
-
-        >.block-left {
-            flex: 1;
-        }
-
-        >.block-right {
-            display: flex;
-            flex-direction: column;
-            width: 240px;
-            background-color: #14181B;
-            border-left: 1px solid #33393D;
-
-            .app-quote-tik {
-                flex: 1;
-                overflow-y: auto;
-            }
-        }
-    }
-}

+ 7 - 9
src/packages/pc/views/market/trade/goods/detail/index.vue

@@ -1,17 +1,19 @@
 <!-- 交易市场-订单交易-详情 -->
 <template>
     <teleport to="#appPageTeleport">
-        <app-view class="g-view-detail">
+        <app-view class="market-trade-goods-detail g-view-detail">
             <template #header>
                 <div class="g-view-detail__header">
-                    <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" link />
+                    <div class="iconbar">
+                        <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" link />
+                    </div>
                     <ul class="breadcrumb" v-if="quote">
-                        <li class="breadcrumb-item">
+                        <li>
                             <span>{{ quote.goodscode }}</span>
                             <span>{{ quote.goodsname }}</span>
                         </li>
                     </ul>
-                    <div class="btnbar">
+                    <div class="buttonbar">
                         <el-button type="primary" @click="active = false" v-if="active">买卖大厅</el-button>
                         <el-button type="primary" @click="active = true" v-else>图表</el-button>
                     </div>
@@ -43,8 +45,4 @@ const quote = futuresStore.getQuoteInfo(props.goodsId)
 const active = shallowRef(true)
 
 const goodsCode = computed(() => quote.value?.goodscode ?? '')
-</script>
-
-<style lang="less">
-@import './index.less';
-</style>
+</script>

+ 2 - 2
src/packages/pc/views/market/trade/spot/index.vue

@@ -51,8 +51,8 @@ filterOptons.selectList = [
 ]
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 5 - 4
src/packages/pc/views/market/trade/spot/order/delisting/index.vue

@@ -1,8 +1,7 @@
 <!-- 交易市场-仓单交易-买卖大厅-摘牌 -->
 <template>
     <app-drawer title="摘牌" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" class="el-form--vertical" label-width="100px" label-position="left" :model="formData"
-            :rules="formRules">
+        <el-form ref="formRef" class="el-form--vertical" label-width="100px" :model="formData" :rules="formRules">
             <el-form-item label="挂牌方">
                 <span>{{ quoteDetail.username }}</span>
             </el-form-item>
@@ -16,8 +15,10 @@
                 <span>{{ holdLB?.enableqty ?? 0 }}</span>
             </el-form-item>
             <el-form-item prop="OrderQty" label="摘牌数量">
-                <el-input-number placeholder="请输入" :min="0" :precision="0" v-model="formData.OrderQty" />
-                <span>{{ quoteDetail.enumdicname }}</span>
+                <div class="el-form-item--col">
+                    <el-input-number placeholder="请输入" :min="0" :precision="0" v-model="formData.OrderQty" />
+                    <span>{{ quoteDetail.enumdicname }}</span>
+                </div>
             </el-form-item>
             <el-form-item label="货款金额">
                 <span>{{ amount }}</span>

+ 8 - 5
src/packages/pc/views/market/trade/spot/order/index.less

@@ -1,8 +1,11 @@
-.market-trade-goods-detail-order {
-    display: flex;
-    height: 100%;
+.market-trade-spot-order {
+    .g-view-detail__container {
+        display: flex;
+        height: 100%;
+        background-color: #181e22;
 
-    .app-table {
-        flex: 1;
+        .app-table {
+            flex: 1;
+        }
     }
 }

+ 32 - 4
src/packages/pc/views/market/trade/spot/order/index.vue

@@ -1,16 +1,44 @@
 <!-- 交易市场-仓单交易-买卖大厅 -->
 <template>
     <teleport to="#appPageTeleport">
-        <app-view class="g-view-detail">
+        <app-view class="market-trade-spot-order g-view-detail">
             <template #header>
                 <div class="g-view-detail__header">
-                    <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" link />
-                    <div class="btnbar">
+                    <div class="iconbar">
+                        <el-button type="primary" icon="ArrowLeftBold" @click="emit('closed')" link />
+                    </div>
+                    <ul class="breadcrumb" v-if="quoteItem">
+                        <li>
+                            <span>{{ quoteItem.deliverygoodsname }}</span>
+                        </li>
+                        <li>
+                            <span>{{ quoteItem.warehousename }}</span>
+                        </li>
+                    </ul>
+                    <ul class="datainfo" v-if="quoteItem">
+                        <li>
+                            <span>卖价:</span>
+                            <span>{{ quoteItem.sellprice }}</span>
+                        </li>
+                        <li>
+                            <span>买价:</span>
+                            <span>{{ quoteItem.buyprice }}</span>
+                        </li>
+                        <li>
+                            <span>卖量:</span>
+                            <span>{{ quoteItem.sellqty }}</span>
+                        </li>
+                        <li>
+                            <span>买量:</span>
+                            <span>{{ quoteItem.buyqty }}</span>
+                        </li>
+                    </ul>
+                    <div class="buttonbar">
                         <el-button type="primary" @click="openComponent('listing')">挂牌求购</el-button>
                     </div>
                 </div>
             </template>
-            <div class="market-trade-spot-order" style="display: flex;height: 100%;">
+            <div class="g-view-detail__container">
                 <app-table class="sell" :data="sellList" v-model:columns="sellColumns" :row-key="rowKey"
                     :expand-row-keys="expandKeys" @row-click="rowClick" showIndex>
                     <!-- 展开行 -->

+ 2 - 3
src/packages/pc/views/market/trade/spot/order/listing/index.vue

@@ -1,8 +1,7 @@
 <!-- 交易市场-仓单交易-买卖大厅-挂牌 -->
 <template>
     <app-drawer title="挂牌" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" class="el-form--vertical" label-width="100px" label-position="left" :model="formData"
-            :rules="formRules">
+        <el-form ref="formRef" class="el-form--vertical" label-width="100px" :model="formData" :rules="formRules">
             <el-form-item prop="PerformanceTemplateID" label="履约方式">
                 <Performance :market-id="17201" v-model="formData.PerformanceTemplateID" />
             </el-form-item>
@@ -24,7 +23,7 @@
         </el-form>
         <template #footer>
             <el-button type="primary" @click="onSubmit">提交</el-button>
-            <el-button @click="onCancel(false)" plain>取消</el-button>
+            <el-button type="info" @click="onCancel(false)">取消</el-button>
         </template>
     </app-drawer>
 </template>

+ 2 - 2
src/packages/pc/views/query/capital/history/index.vue

@@ -39,8 +39,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 2 - 2
src/packages/pc/views/query/order/goods/history/index.vue

@@ -55,8 +55,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 2 - 2
src/packages/pc/views/query/order/spot/history/index.vue

@@ -69,8 +69,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 2 - 2
src/packages/pc/views/query/trade/goods/history/index.vue

@@ -56,8 +56,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 2 - 2
src/packages/pc/views/query/trade/spot/history/index.vue

@@ -58,8 +58,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 filterOptons.buttonList = [
-    { lable: '重置', onClick: () => onSearch(true) },
-    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() }
+    { lable: '查询', className: 'el-button--info', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--info', onClick: () => onSearch(true) }
 ]
 
 const onSearch = (clear = false) => {

+ 4 - 0
src/types/model/performance.d.ts

@@ -27,6 +27,8 @@ declare namespace Model {
         buyerfreezeamount: number
         /// 履约冻结剩余(买履约)
         buyerfreezeamountremain: number
+        /// 买方联系信息 - 存JSON字符串, 根据枚举‘BuyerContactInfo’,显示\隐藏字段,若数据不为JSON,则直接显示{ "ContactInfo": "xxxxxxxx", "ReceiveInfo": "xxxxxxxxx", "ReceiptInfo": "xxxxxxxxxxxx"}
+        buyerinfo: string
         /// 买卖方向 0-买 1-卖
         buyorsell: number
         /// 买方已冻/已扣金额 (已付金额)
@@ -99,6 +101,8 @@ declare namespace Model {
         sellerfreezeamount: number
         /// 履约冻结剩余(卖履约)
         sellerfreezeamountremain: number
+        /// 卖方联系信息 - 存JSON字符串, 根据枚举‘SellerContactInfo’,显示\隐藏字段,若数据不为JSON,则直接显示{ "ContactInfo": "xxxxxxxx"}
+        sellerinfo: string
         /// 卖方已收金额
         sellreceivedamount: number
         /// 卖方名字