li.shaoyi 1 vuosi sitten
vanhempi
commit
16bc121f8d

+ 10 - 0
src/constants/admin.ts

@@ -17,4 +17,14 @@ export function getAdminStatusList() {
         { label: '停用', value: AdminStatus.Disable },
         { label: '锁定', value: AdminStatus.Lock },
     ]
+}
+
+/**
+ * 获取管理员状态名称
+ * @param value 
+ * @returns 
+ */
+export function getAdminStatusName(value?: number) {
+    const item = getAdminStatusList().find((e) => e.value === value)
+    return item?.label ?? value
 }

+ 44 - 16
src/filters/index.ts

@@ -59,6 +59,40 @@ export function handleNumberValue(value: number | string = 0, suffix = '') {
 }
 
 /**
+ * 从数组中创建树列表
+ * @param data 
+ * @param idKey 
+ * @param parentIdKey 
+ * @param filter 
+ * @returns 
+ */
+export function buildTree<T>(data: T[], idKey: keyof T, parentIdKey: keyof T, filter: (node: T) => boolean = () => true) {
+    const map = new Map<T[keyof T], T & { children: T[] }>()
+    for (const item of data) {
+        const id = item[idKey]
+        map.set(id, { ...item, children: [] })
+    }
+    for (const item of data) {
+        const id = item[idKey]
+        const parentId = item[parentIdKey]
+        if (parentId !== id) {
+            const currentNode = map.get(id)
+            const parentNode = map.get(parentId)
+            if (currentNode && parentNode) {
+                parentNode.children.push(currentNode)
+            }
+        }
+    }
+    const nodes = []
+    for (const node of map.values()) {
+        if (filter(node)) {
+            nodes.push(node)
+        }
+    }
+    return nodes
+}
+
+/**
  * 查找树形结构中某个节点
  * @param data 
  * @param value 
@@ -66,17 +100,14 @@ export function handleNumberValue(value: number | string = 0, suffix = '') {
  * @returns 
  */
 export function findTreeNodeById<T extends object, K extends keyof T>(data: T[], value: T[K], props?: Partial<{ id: K, children: K }>): T | undefined {
-    const defaultProps = {
-        id: props?.id ?? 'id' as K,
-        children: props?.children ?? 'children' as K
-    }
+    const { id = 'id' as K, children = 'children' as K } = props ?? {}
     for (const item of data) {
-        if (item[defaultProps.id] === value) {
+        if (item[id] === value) {
             return item
         }
-        const children = item[defaultProps.children]
-        if (Array.isArray(children)) {
-            const res = findTreeNodeById(children, value, props)
+        const nodes = item[children]
+        if (Array.isArray(nodes)) {
+            const res = findTreeNodeById(nodes, value, props)
             if (res) return res
         }
     }
@@ -91,17 +122,14 @@ export function findTreeNodeById<T extends object, K extends keyof T>(data: T[],
  * @returns 
  */
 export function hasValueInTree<T extends object, K extends keyof T>(data: T[], value: T[K], props?: Partial<{ id: K, children: K }>) {
-    const defaultProps = {
-        id: props?.id ?? 'id' as K,
-        children: props?.children ?? 'children' as K
-    }
+    const { id = 'id' as K, children = 'children' as K } = props ?? {}
     const checkItem = (item: T) => {
-        if (item[defaultProps.id] === value) {
+        if (item[id] === value) {
             return true
         }
-        const children = item[defaultProps.children]
-        if (Array.isArray(children)) {
-            return children.some(checkItem)
+        const nodes = item[children]
+        if (Array.isArray(nodes)) {
+            return nodes.some(checkItem)
         }
         return false
     }

+ 14 - 9
src/hooks/enum/index.ts

@@ -20,20 +20,25 @@ export function useEnum(code: string) {
     // 获取枚举列表
     const getEnumOptions = (filtered: number | number[] = [], reverse = false) => {
         const valuesSet = new Set(Array.isArray(filtered) ? filtered : [filtered])
-        const result = state.enums.map((e) => ({
-            label: e.enumdicname,
-            value: e.enumitemname
-        }))
-        if (valuesSet.size > 0) {
+        if (valuesSet.size === 0) {
+            return state.enums.map((e) => ({
+                label: e.enumdicname,
+                value: e.enumitemname
+            }))
+        }
+        const result = state.enums.filter((e) => {
             if (reverse) {
                 // 排除枚举
-                return result.filter((e) => !valuesSet.has(e.value))
+                return !valuesSet.has(e.enumitemname)
             } else {
                 // 匹配枚举
-                return result.filter((e) => valuesSet.has(e.value))
+                return valuesSet.has(e.enumitemname)
             }
-        }
-        return result
+        })
+        return result.map((e) => ({
+            label: e.enumdicname,
+            value: e.enumitemname
+        }))
     }
 
     // 注册成功回调

+ 26 - 7
src/hooks/market/index.ts

@@ -1,4 +1,4 @@
-import { reactive, toRefs, computed, onUnmounted } from 'vue'
+import { reactive, toRefs, onUnmounted } from 'vue'
 import { orderListAll } from '@/services/api/common'
 
 export function useMarket() {
@@ -7,11 +7,6 @@ export function useMarket() {
         markets: <Model.OrderListAllRsp[]>[]
     })
 
-    const marketOptions = computed(() => state.markets.map((e) => ({
-        label: e.marketname,
-        value: e.marketid
-    })))
-
     const controller = new AbortController()
     const readyCallbackMap = new Map<symbol, () => void>()
 
@@ -19,6 +14,30 @@ export function useMarket() {
         return state.markets.find((e) => e.marketid === marketId)
     }
 
+    // 获取市场列表
+    const getMarketOptions = (filtered: number | number[] = [], reverse = false) => {
+        const valuesSet = new Set(Array.isArray(filtered) ? filtered : [filtered])
+        if (valuesSet.size === 0) {
+            return state.markets.map((e) => ({
+                label: e.marketname,
+                value: e.marketid
+            }))
+        }
+        const result = state.markets.filter((e) => {
+            if (reverse) {
+                // 排除市场
+                return !valuesSet.has(e.trademode) && !valuesSet.has(e.marketid)
+            } else {
+                // 匹配市场
+                return valuesSet.has(e.trademode) || valuesSet.has(e.marketid)
+            }
+        })
+        return result.map((e) => ({
+            label: e.marketname,
+            value: e.marketid
+        }))
+    }
+
     // 注册成功回调
     const registerMarketReadyCallback = (callback: () => void) => {
         if (state.loading) {
@@ -45,7 +64,7 @@ export function useMarket() {
 
     return {
         ...toRefs(state),
-        marketOptions,
+        getMarketOptions,
         getMarketById,
         registerMarketReadyCallback
     }

+ 4 - 1
src/packages/pc/components/base/upload/index.vue

@@ -21,6 +21,7 @@
 import { shallowRef, shallowReactive, computed, PropType, onMounted } from 'vue'
 import { ElMessage, UploadProps, UploadRawFile, UploadUserFile, UploadFile, UploadFiles, UploadInstance, genFileId } from 'element-plus'
 import { useLoginStore } from '@/stores'
+import { localData } from '@/stores/storage'
 import service from '@/services'
 import cryptojs from 'crypto-js'
 
@@ -66,8 +67,10 @@ const loginStore = useLoginStore()
 const timestamp = new Date().getTime()
 
 const headers = {
-    Authorization: loginStore.token,
+    'Accept-Language': localData.getValue('appLanguage'),
+    'Sign-Id': loginStore.signId,
     Sign: cryptojs.SHA256(loginStore.sign + timestamp.toString()).toString(),
+    Authorization: loginStore.token,
     Timestamp: timestamp.toString(),
 }
 

+ 9 - 3
src/packages/pc/views/account/tradeacct/components/details/index.vue

@@ -31,10 +31,16 @@ const props = defineProps({
 const show = shallowRef(true)
 const refresh = shallowRef(false)
 
+// 账户类型
+const mainaccounttypeEnum = useEnum('mainaccounttype')
+// 内/外部
+const accounttypeEnum = useEnum('accounttype')
 // 交易状态
 const traderstatusEnum = useEnum('traderstatus')
 // 签约状态
 const signstatusEnum = useEnum('signstatus')
+// 变动标志
+const flagEnum = useEnum('flag')
 
 const { data } = useRequest(queryTaAccountDetail, {
     params: {
@@ -48,10 +54,10 @@ const { data } = useRequest(queryTaAccountDetail, {
 const detailProps: CellProp[] = [
     { prop: 'accountId', label: '资金账户:' },
     { prop: 'accountName', label: '所属机构:' },
-    { prop: 'isMain', label: '是否母账户:' },
+    { prop: 'isMain', label: '是否母账户:', formatValue: (val) => mainaccounttypeEnum.getEnumTypeName(val) },
     { prop: 'currency', label: '币种:' },
-    { prop: 'taAccountType', label: '内外部:' },
-    { prop: 'changeFlag', label: '变动标志:' },
+    { prop: 'taAccountType', label: '内外部:', formatValue: (val) => accounttypeEnum.getEnumTypeName(val) },
+    { prop: 'changeFlag', label: '变动标志:', formatValue: (val) => flagEnum.getEnumTypeName(val) },
     { prop: 'tradeStatus', label: '交易状态:', formatValue: (val) => traderstatusEnum.getEnumTypeName(val) },
     { prop: 'currentBalance', label: '期末余额:', decimal: 2 },
     { prop: 'payCharge', label: '平台服务费:', decimal: 2 },

+ 6 - 2
src/packages/pc/views/account/tradeacct/index.vue

@@ -33,6 +33,10 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
+// 账户类型
+const mainaccounttypeEnum = useEnum('mainaccounttype')
+// 内/外部
+const accounttypeEnum = useEnum('accounttype')
 // 交易状态
 const traderstatusEnum = useEnum('traderstatus')
 
@@ -54,8 +58,8 @@ const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryT
 
 const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'accountId', label: '资金账号' },
-    { field: 'isMain', label: '账户类型' },
-    { field: 'taAccountType', label: '内/外部' },
+    { field: 'isMain', label: '账户类型', formatValue: (val) => mainaccounttypeEnum.getEnumTypeName(val) },
+    { field: 'taAccountType', label: '内/外部', formatValue: (val) => accounttypeEnum.getEnumTypeName(val) },
     { field: 'relatedName', label: '关联账户' },
     { field: 'parentAccountId', label: '所属母账户' },
     { field: 'tradeStatus', label: '交易状态', formatValue: (val) => traderstatusEnum.getEnumTypeName(val) },

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

@@ -28,6 +28,7 @@
 import { shallowRef } from 'vue'
 import { ElMessage } from 'element-plus'
 import { formatDate } from '@/filters'
+import { useEnum } from '@/hooks/enum'
 import { useRequest } from '@/hooks/request'
 import { useOperation } from '@/hooks/operation'
 import { queryRole } from '@/services/api/admin'
@@ -35,6 +36,9 @@ import AppTable from '@pc/components/base/table/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
+// 状态
+const userstatusEnum = useEnum('userstatus')
+
 const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.RoleRsp>({
     onClose: () => onRefresh()
 })
@@ -55,7 +59,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'rolename', label: '角色名称' },
     { field: 'modifiername', label: '创建人' },
     { field: 'modifytime', label: '创建时间' },
-    { field: 'rolestatus', label: '状态' },
+    { field: 'rolestatus', label: '状态', formatValue: (val) => userstatusEnum.getEnumTypeName(val) },
     { field: 'operate', label: '操作', fixed: 'right' }
 ])
 

+ 5 - 5
src/packages/pc/views/admin/user/index.vue

@@ -27,8 +27,8 @@
                     @change="onSearch" />
             </template>
         </app-table>
-        <component :is="componentMap.get(componentId)" v-bind="{ code: componentId, record }"
-            @closed="closeComponent" v-if="componentId" />
+        <component :is="componentMap.get(componentId)" v-bind="{ code: componentId, record }" @closed="closeComponent"
+            v-if="componentId" />
     </app-view>
 </template>
 
@@ -39,7 +39,7 @@ import { formatDate } from '@/filters'
 import { useRequest } from '@/hooks/request'
 import { useDataFilter } from '@/hooks/datatable'
 import { useOperation } from '@/hooks/operation'
-import { AdminStatus, getAdminStatusList } from '@/constants/admin'
+import { AdminStatus, getAdminStatusName, getAdminStatusList } from '@/constants/admin'
 import { querySystemmanager } from '@/services/api/admin'
 import { queryOrganSelect } from '@/services/api/common'
 import { useUserStore } from '@/stores'
@@ -85,9 +85,9 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'username', label: '用户名称' },
     { field: 'areaName', label: '所属机构' },
     { field: 'roleName', label: '角色' },
-    { field: 'managerstatus', label: '账户状态' },
+    { field: 'managerstatus', label: '账户状态', formatValue: (val) => getAdminStatusName(val) },
     { field: 'modifytime', label: '创建时间' },
-    { field: 'operate', label: '操作', fixed: 'right' }
+    { field: 'operate', label: '操作', fixed: 'right', width: 150 }
 ])
 
 filterOptons.selectList = [

+ 52 - 0
src/packages/pc/views/investor/custom/tradecfg/components/delete/index.vue

@@ -0,0 +1,52 @@
+<!-- 交易商管理-个性化管理-交易商个性化-删除 -->
+<template>
+    <app-drawer title="提示" v-model:show="show" :loading="loading" :refresh="refresh">
+        <div class="g-text-message">是否确认删除该商品个性化配置?</div>
+        <template #footer>
+            <el-button @click="onCancel(false)">取消</el-button>
+            <el-button type="primary" @click="onSubmit">确认</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType } from 'vue'
+import { ElMessage } from 'element-plus'
+import { deleteTraderConfig } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.TradeConfigViewRsp>,
+        required: true
+    }
+})
+
+const show = shallowRef(true)
+const refresh = shallowRef(false)
+const loading = shallowRef(false)
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    loading.value = true
+    deleteTraderConfig({
+        data: {
+            usergroupid: props.record.usergroupid,
+            marketid: props.record.marketid,
+            goodsid: props.record.goodsid
+        }
+    }).then(() => {
+        ElMessage.success('删除成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('删除失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 274 - 0
src/packages/pc/views/investor/custom/tradecfg/components/edit/index.vue

@@ -0,0 +1,274 @@
+<!-- 交易商管理-个性化管理-交易商个性化-编辑 -->
+<template>
+    <app-drawer title="编辑" width="1100" v-model:show="show" :refresh="refresh" :loading="loading">
+        <el-form ref="formRef" label-width="140px" :model="formData" :rules="formRules" :show-message="false">
+            <fieldset class="g-fieldset el-form--horizontal">
+                <legend class="g-fieldset__legend">基本信息设置</legend>
+                <el-form-item label="分组" prop="usergroupid">
+                    <el-select v-model="formData.usergroupid" @change="onGroupChange">
+                        <template v-for="(item, index) in groupTrees" :key="index">
+                            <el-option :label="item.name" :value="item.usergroupid" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="市场" prop="marketid">
+                    <el-select v-model="formData.marketid" :empty-values="[null, undefined, 0]"
+                        @change="onMarketChange">
+                        <template v-for="(item, index) in marketData?.markets" :key="index">
+                            <el-option :label="item.marketname" :value="item.marketid" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="商品" prop="goodsid">
+                    <el-select v-model="formData.goodsid" :empty-values="[null, undefined, 0]" @change="onGoodsChange">
+                        <template v-for="(item, index) in goodsData?.goods" :key="index">
+                            <el-option :label="item.goodsname" :value="item.goodsid" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="保证金类别" prop="paramid">
+                    <el-select v-model="formData.paramid" :empty-values="[null, undefined, 0]" @change="onResultChange"
+                        clearable>
+                        <template v-for="(item, index) in resultData?.results" :key="index">
+                            <el-option :label="item.enumdicname" :value="item.enumitemname" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+            </fieldset>
+            <app-table :data="resultData?.rulesConfigVos" :columns="ruleColumns">
+                <!-- 商品设置值 -->
+                <template #paramvalue="{ row }">
+                    <span v-if="row.ruleid === 105">{{ feetypeEnum.getEnumTypeName(row.paramvalue) }}</span>
+                    <span v-else>{{ row.paramvalue || 0 }}</span>
+                </template>
+                <!-- 会员 -->
+                <template #dvalue="{ row }">
+                    <span v-if="row.ruleid === 105">
+                        {{ handleNoneValue(feetypeEnum.getEnumTypeName(row.dvalue)) }}
+                    </span>
+                    <span v-else>{{ handleNoneValue(row.dvalue) }}</span>
+                </template>
+                <!-- 个性化 -->
+                <template #cvalue="{ row, index }">
+                    <template v-if="formData.ruleList[index]">
+                        <el-select v-model="formData.ruleList[index].paramvalue" v-if="row.ruleid === 105">
+                            <template v-for="item in feetypeEnum.getEnumOptions()" :key="item.id">
+                                <el-option :label="item.label" :value="item.value" />
+                            </template>
+                        </el-select>
+                        <el-input-number v-model="formData.ruleList[index].paramvalue" placeholder="请输入" v-else />
+                    </template>
+                </template>
+            </app-table>
+            <app-table :data="resultData?.feesConfigVos" :columns="feeColumns">
+                <!-- 商品设置值 -->
+                <template #exchangevalue="{ row }">
+                    <template v-if="row.feealgorithm">
+                        {{ row.exchangevalue + (row.feealgorithm === 1 && '‱') }}
+                    </template>
+                    <template v-else>
+                        {{ handleNoneValue() }}
+                    </template>
+                </template>
+                <!-- 会员设置值 -->
+                <template #dvalue="{ row }">
+                    <template v-if="row.drithm">
+                        {{ row.dvalue + (row.drithm === 1 && '‱') }}
+                    </template>
+                    <template v-else>
+                        {{ handleNoneValue() }}
+                    </template>
+                </template>
+                <!-- 最小值 -->
+                <template #memberminvalue="{ row }">
+                    {{ row.memberminvalue + (row.feealgorithm === 1 && '‱') }}
+                </template>
+                <!-- 最大值 -->
+                <template #membermaxvalue="{ row }">
+                    {{ row.membermaxvalue + (row.feealgorithm === 1 && '‱') }}
+                </template>
+                <!-- 服务费 -->
+                <template #fee="{ row, index }">
+                    <el-input-number v-model="formData.feeList[index].paramvalue" :min="row.memberminvalue"
+                        :max="row.membermaxvalue" placeholder="请输入" />
+                </template>
+            </app-table>
+        </el-form>
+        <template #footer>
+            <el-button @click="onCancel(false)">关闭</el-button>
+            <el-button type="primary" @click="onSubmit">保存</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 { parseTenThousand, handleNoneValue } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { traderPersonAdd, getInvestorTree, initInvestorPerson } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppTable from '@pc/components/base/table/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.TradeConfigViewRsp>
+    }
+})
+
+const feetypeEnum = useEnum('feetype')
+
+const formRef = ref<FormInstance>()
+const show = ref(true)
+const refresh = ref(false)
+const loading = ref(false)
+
+const formData = ref<Model.TraderPersonAddReq>({
+    feeList: [],
+    flag: 0,
+    goodsid: 0,
+    marketid: 0,
+    ruleList: [],
+    usergroupid: 0
+})
+
+// 分组树列表
+const groupTrees = computed(() => {
+    return nodeList.value.filter((e) => e.pid === '-1')
+})
+
+// 获取分组列表
+const { dataList: nodeList } = useRequest(getInvestorTree)
+
+// 获取市场列表
+const { data: marketData, run: getMarketList } = useRequest(initInvestorPerson)
+
+// 获取商品列表
+const { data: goodsData, run: getGoodsList } = useRequest(initInvestorPerson, {
+    manual: true,
+})
+
+// 获取保证金类别列表
+const { data: resultData, run: getResultList } = useRequest(initInvestorPerson, {
+    manual: true,
+    onSuccess: (res) => {
+        formData.value.ruleList = []
+        formData.value.feeList = []
+
+        res.data.rulesConfigVos.forEach((e) => {
+            formData.value.ruleList.push({
+                paramid: e.ruleid,
+                paramvalue: 0
+            })
+        })
+
+        res.data.feesConfigVos.forEach((e) => {
+            formData.value.feeList.push({
+                feealgorithm: e.feealgorithm,
+                paramid: e.tradefeeid,
+                paramvalue: 0
+            })
+        })
+    }
+})
+
+const ruleColumns = computed<Model.TableColumn[]>(() => [
+    { field: 'rulename', label: '扩展项' },
+    { field: 'paramvalue', label: '商品设置值' },
+    { field: 'dvalue', label: '会员设置值', show: formData.value.usergroupid !== 0 },
+    { field: 'cvalue', label: '个性化' },
+])
+
+const feeColumns = computed<Model.TableColumn[]>(() => [
+    { field: 'tradefeename', label: '费用项' },
+    { field: 'feealgorithm', label: '费用算法', formatValue: (val) => val === 1 ? '比率' : '固定值' },
+    { field: 'exchangevalue', label: '商品设置值' },
+    { field: 'dvalue', label: '会员设置值', show: formData.value.usergroupid !== 0 },
+    { field: 'memberminvalue', label: '最小值' },
+    { field: 'fee', label: '服务费', width: 200 },
+    { field: 'membermaxvalue', label: '最大值' },
+])
+
+// 表单验证规则
+const formRules: FormRules = {
+    usergroupid: [{ required: true }],
+    marketid: [{ required: true }],
+    goodsid: [{ required: true }],
+}
+
+// 选择分组
+const onGroupChange = (value: number) => {
+    formData.value.marketid = 0
+    formData.value.goodsid = 0
+    formData.value.paramid = 0
+    formData.value.feealgorithm = 0
+    resultData.value = undefined
+    getMarketList({
+        usergroupid: value,
+    })
+}
+
+// 选择市场
+const onMarketChange = (value: number) => {
+    formData.value.goodsid = 0
+    formData.value.paramid = 0
+    formData.value.feealgorithm = 0
+    resultData.value = undefined
+    getGoodsList({
+        usergroupid: formData.value.usergroupid,
+        marketid: value,
+    })
+}
+
+// 选择商品
+const onGoodsChange = (value: number) => {
+    formData.value.paramid = 0
+    formData.value.feealgorithm = 0
+    getResultList({
+        marketid: formData.value.marketid,
+        usergroupid: formData.value.usergroupid,
+        goodsid: value
+    })
+}
+
+// 选择保证金类别
+const onResultChange = (value?: number) => {
+    const res = resultData.value?.results.find((e) => e.enumitemname === value?.toString())
+    formData.value.feealgorithm = res?.marginalgorithm
+}
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            loading.value = true
+            traderPersonAdd({
+                data: formData.value
+            }).then(() => {
+                ElMessage.success('保存成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('保存失败:' + err)
+            }).finally(() => {
+                loading.value = false
+            })
+        }
+    })
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+onMounted(() => {
+    const record = props.record
+    if (record) {
+        // formData.value = {
+        //     accountid: record.accountid,
+        //     customertype: record.customertype,
+        //     userid: record.userid
+        // }
+    }
+})
+</script>

+ 212 - 1
src/packages/pc/views/investor/custom/tradecfg/index.vue

@@ -1,7 +1,218 @@
 <!-- 交易商管理-个性化管理-交易商个性化 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <el-form ref="formRef" class="el-form--filter" :model="qs" :rules="formRules" :show-message="false">
+                <el-form-item label="分组" prop="usergroupid">
+                    <el-select v-model="selectedGroup" value-key="id" :validate-event="false" @change="onGroupChange">
+                        <template v-for="item in groupList" :key="item.id">
+                            <el-option :label="item.name" :value="item" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="市场" prop="marketid">
+                    <el-select v-model="selectedMarket" value-key="id" :validate-event="false" @change="onMarketChange">
+                        <template v-for="item in marketList" :key="item.id">
+                            <el-option :label="item.name" :value="item" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="商品" prop="goodsid">
+                    <el-select v-model="selectedGoods" value-key="id" :validate-event="false">
+                        <template v-for="item in goodsList" :key="item.id">
+                            <el-option :label="item.name" :value="item" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item>
+                    <el-button type="primary" @click="onSearch">查询</el-button>
+                </el-form-item>
+            </el-form>
+        </template>
+        <div style="display: flex;justify-content: space-between;">
+            <app-operation :data-list="getFilteredButtons(['investor_custom_tradecfg_add'])" @click="openComponent" />
+            <app-operation
+                :data-list="getFilteredButtons(['investor_custom_tradecfg_modify', 'investor_custom_tradecfg_delete'])"
+                @click="openComponent" v-if="data" />
+        </div>
+        <template v-if="data">
+            <app-table-details title="基本信息" :data="data" :label-width="160" :cell-props="detailProps" :column="3" />
+            <app-table :data="data.druleList" :columns="ruleColumns">
+                <!-- 商品设置值 -->
+                <template #paramvalue="{ row }">
+                    <span v-if="row.ruleid === 105">{{ feetypeEnum.getEnumTypeName(row.paramvalue) }}</span>
+                    <span v-else>{{ row.paramvalue || 0 }}</span>
+                </template>
+                <!-- 会员设置值 -->
+                <template #dvalue="{ row }">
+                    <span v-if="row.ruleid === 105">
+                        {{ handleNoneValue(feetypeEnum.getEnumTypeName(row.dvalue)) }}
+                    </span>
+                    <span v-else>{{ handleNoneValue(row.dvalue) }}</span>
+                </template>
+                <!-- 个性化 -->
+                <template #cvalue="{ row }">
+                    <span v-if="row.ruleid === 105">
+                        {{ handleNoneValue(feetypeEnum.getEnumTypeName(row.cvalue)) }}
+                    </span>
+                    <span v-else>{{ handleNoneValue(row.cvalue) }}</span>
+                </template>
+            </app-table>
+            <app-table :data="data.dfeeList" :columns="feeColumns">
+                <!-- 商品设置值 -->
+                <template #exchangevalue="{ row }">
+                    <template v-if="row.feealgorithm">
+                        <span v-if="row.feealgorithm === 1">{{ parseTenThousand(row.exchangevalue) }}</span>
+                        <span v-else>{{ row.exchangevalue }}</span>
+                    </template>
+                    <template v-else>
+                        {{ handleNoneValue() }}
+                    </template>
+                </template>
+                <!-- 会员设置值 -->
+                <template #dvalue="{ row }">
+                    <template v-if="row.drithm">
+                        <span v-if="row.drithm === 1">{{ parseTenThousand(row.dvalue) }}</span>
+                        <span v-else>{{ row.dvalue }}</span>
+                    </template>
+                    <template v-else>
+                        {{ handleNoneValue() }}
+                    </template>
+                </template>
+                <!-- 个性化 -->
+                <template #cvalue="{ row }">
+                    <template v-if="row.crithm">
+                        <span v-if="row.crithm === 1">{{ parseTenThousand(row.cvalue) }}</span>
+                        <span v-else>{{ row.cvalue }}</span>
+                    </template>
+                    <template v-else>
+                        {{ handleNoneValue() }}
+                    </template>
+                </template>
+            </app-table>
+        </template>
+        <el-empty v-else />
+        <component :is="componentMap.get(componentId)" v-bind="{ record: toRaw(data) }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef, computed, nextTick, toRaw } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { parseTenThousand, handleNoneValue } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useOperation } from '@/hooks/operation'
+import { getInvestorTree, tradeConfigView } from '@/services/api/investor'
+import { CellProp } from '@pc/components/base/table-details/types'
+import AppTable from '@pc/components/base/table/index.vue'
+import AppOperation from '@pc/components/base/operation/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+
+const { componentMap, componentId, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.TradeConfigViewRsp>({
+    onClose: (componentId) => {
+        getNodeList()
+        if (componentId === 'investor_custom_tradecfg_delete') {
+            qs.value = {}
+            data.value = undefined
+        }
+    }
+})
+
+const customerTypeEnum = useEnum('customerType')
+const feetypeEnum = useEnum('feetype')
+
+const formRef = shallowRef<FormInstance>()
+const qs = shallowRef<Partial<Model.TradeConfigViewReq>>({})
+const info = shallowRef<Partial<{
+    groupName: string,
+    marketName: string,
+    goodsName: string
+}>>({})
+
+const selectedGroup = shallowRef<Model.InvestorTreeRsp>() // 选中的分组对象
+const selectedMarket = shallowRef<Model.InvestorTreeRsp>() // 选中的市场对象
+const selectedGoods = shallowRef<Model.InvestorTreeRsp>() // 选中的商品对象
+
+// 分组列表
+const groupList = computed(() => nodeList.value.filter((e) => e.pid === '-1'))
+// 市场列表
+const marketList = computed(() => nodeList.value.filter((e) => e.pid === selectedGroup.value?.id))
+// 商品列表
+const goodsList = computed(() => nodeList.value.filter((e) => e.pid === selectedMarket.value?.id))
+
+const { dataList: nodeList, run: getNodeList } = useRequest(getInvestorTree, {
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const { data, run } = useRequest(tradeConfigView, {
+    manual: true,
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const detailProps: CellProp[] = [
+    { prop: 'usergroupid', label: '分组:', formatValue: () => info.value.groupName },
+    { prop: 'marketid', label: '市场:', formatValue: () => info.value.marketName },
+    { prop: 'goodsid', label: '商品:', formatValue: () => info.value.goodsName },
+    { prop: 'paramid', label: '保证金类别:', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+    { prop: 'marketmarginvaluedisplay', label: '收取方式:' },
+]
+
+const ruleColumns = computed<Model.TableColumn[]>(() => [
+    { field: 'rulename', label: '扩展项' },
+    { field: 'paramvalue', label: '商品设置值' },
+    { field: 'dvalue', label: '会员设置值', show: qs.value.usergroupid !== 0 },
+    { field: 'cvalue', label: '个性化' },
+])
+
+const feeColumns = computed<Model.TableColumn[]>(() => [
+    { field: 'tradefeename', label: '费用项' },
+    { field: 'feealgorithm', label: '费用算法', formatValue: (val) => val === 1 ? '比率' : '固定值' },
+    { field: 'exchangevalue', label: '商品设置值' },
+    { field: 'dvalue', label: '会员设置值', show: qs.value.usergroupid !== 0 },
+    { field: 'cvalue', label: '个性化' },
+])
+
+// 表单验证规则
+const formRules: FormRules = {
+    usergroupid: [{ required: true }],
+    marketid: [{ required: true }],
+    goodsid: [{ required: true }],
+}
+
+// 选择分组
+const onGroupChange = () => {
+    selectedMarket.value = undefined
+    selectedGoods.value = undefined
+}
+
+// 选择市场
+const onMarketChange = () => {
+    selectedGoods.value = undefined
+}
+
+const onSearch = () => {
+    qs.value = {
+        usergroupid: selectedGroup.value?.usergroupid,
+        marketid: selectedMarket.value?.marketid,
+        goodsid: selectedGoods.value?.goodsid,
+    }
+    nextTick(() => {
+        formRef.value?.validate((valid) => {
+            if (valid) {
+                info.value = {
+                    groupName: selectedGroup.value?.name,
+                    marketName: selectedMarket.value?.name,
+                    goodsName: selectedGoods.value?.name,
+                }
+                run(qs.value)
+            }
+        })
+    })
+}
 </script>

+ 9 - 5
src/packages/pc/views/member/subinstitution/manage/components/details/index.vue

@@ -61,9 +61,9 @@
             </template>
             <!-- 性别 -->
             <template #sex="{ value }">
-                <p>{{ value }}</p>
+                <p>{{ getGenderName(value) }}</p>
                 <p class="red" v-if="newData && newData.userinfoDetailVo.sex !== value">
-                    {{ newData.userinfoDetailVo.sex }}
+                    {{ getGenderName(newData.userinfoDetailVo.sex) }}
                 </p>
             </template>
             <!-- 手机号 -->
@@ -189,7 +189,7 @@ import { ref, PropType, computed } from 'vue'
 import { handleNoneValue } from '@/filters'
 import { decryptAES } from '@/services/crypto'
 import { useEnum } from '@/hooks/enum'
-import { UserInfoType } from '@/constants/member'
+import { UserInfoType, getGenderName } from '@/constants/member'
 import { useRequest } from '@/hooks/request'
 import { organSonViewson } from '@/services/api/member'
 import { CellProp } from '@pc/components/base/table-details/types'
@@ -214,10 +214,14 @@ const oldData = ref<Model.organSonViewsonRsp['oldResult']>()
 
 // 机构状态
 const areastatusEnum = useEnum('areastatus')
+// 机构类型
+const usertype2Enum = useEnum('usertype2')
 // 企业性质枚举
 const biznatureEnum = useEnum('biznature')
 // 证件类型
 const certificatetypeEnum = useEnum('certificatetype')
+// 用户状态
+const userstatusEnum = useEnum('userstatus')
 
 const { data } = useRequest(organSonViewson, {
     params: {
@@ -269,7 +273,7 @@ const { data } = useRequest(organSonViewson, {
 const detailProps1: CellProp[] = [
     { prop: 'userId', label: '机构代码:' },
     { prop: 'accountName', label: '机构名称:' },
-    { prop: 'usertype', label: '机构类型:' },
+    { prop: 'usertype', label: '机构类型:', formatValue: (val) => usertype2Enum.getEnumTypeName(val) },
     { prop: 'accountStatus', label: '机构状态:', formatValue: (val) => areastatusEnum.getEnumTypeName(val) },
     { prop: 'auditAccountName', label: '审核人:' },
     { prop: 'auditremark', label: '审核备注:' },
@@ -348,7 +352,7 @@ const detailProps5 = computed<CellProp[]>(() => {
         return [
             { prop: 'logincode', label: '管理员登录帐号:' },
             { prop: 'username', label: '管理员名称:' },
-            { prop: 'userstatus', label: '帐号状态:' },
+            { prop: 'userstatus', label: '帐号状态:', formatValue: (val) => userstatusEnum.getEnumTypeName(val) },
         ]
     }
     return []

+ 9 - 22
src/packages/pc/views/member/subinstitution/manage/index.vue

@@ -35,7 +35,8 @@
 <script lang="ts" setup>
 import { shallowRef, computed } from 'vue'
 import { ElMessage } from 'element-plus'
-import { formatDate } from '@/filters'
+import { formatDate, buildTree } from '@/filters'
+import { getUserInfoTypeName } from '@/constants/member'
 import { useEnum } from '@/hooks/enum'
 import { useOperation } from '@/hooks/operation'
 import { useRequest } from '@/hooks/request'
@@ -48,6 +49,8 @@ import AppPagination from '@pc/components/base/pagination/index.vue'
 
 // 机构状态
 const areastatusEnum = useEnum('areastatus')
+// 机构类型
+const usertype2Enum = useEnum('usertype2')
 
 const treeRef = shallowRef()
 const selectedParent = shallowRef<Model.AreaAndAllChildsRsp>() // 已选中的树节点
@@ -57,24 +60,8 @@ const { componentMap, componentId, record, openComponent, closeComponent, getFil
 })
 
 const treeList = computed(() => {
-    const map = new Map()
-    for (const item of nodeList.value) {
-        map.set(item.userid, { ...item, children: [] })
-    }
-    for (const item of nodeList.value) {
-        if (item.parentuserid !== item.userid) {
-            const parent = map.get(item.parentuserid)
-            if (parent) {
-                parent.children.push(map.get(item.userid))
-            }
-        }
-    }
-    const roots = []
-    for (const [id, node] of map) {
-        roots.push(node)
-        break
-    }
-    return roots
+    const [firstNode] = buildTree(nodeList.value, 'userid', 'parentuserid')
+    return firstNode ? [firstNode] : []
 })
 
 const { dataList: nodeList, run: getNodeList } = useRequest(getAreaAndAllChilds, {
@@ -112,7 +99,7 @@ const detailProps: CellProp[] = [
     { prop: 'userid', label: '机构代码:' },
     { prop: 'accountname', label: '机构名称:' },
     { prop: 'accountstatus', label: '机构状态:', formatValue: (val) => areastatusEnum.getEnumTypeName(val) },
-    { prop: 'userinfotype', label: '所有者类型:' },
+    { prop: 'userinfotype', label: '所有者类型:', formatValue: (val) => getUserInfoTypeName(val) },
     { prop: 'contactname', label: '联系人:' },
     { prop: 'pathname', label: '地址:' },
 ]
@@ -121,8 +108,8 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'userid', label: '机构代码' },
     { field: 'accountname', label: '机构名称' },
     { field: 'accountstatus', label: '机构状态', formatValue: (val) => areastatusEnum.getEnumTypeName(val) },
-    { field: 'usertype', label: '机构类型' },
-    { field: 'userinfotype', label: '所有者类型' },
+    { field: 'usertype', label: '机构类型', formatValue: (val) => usertype2Enum.getEnumTypeName(val) },
+    { field: 'userinfotype', label: '所有者类型', formatValue: (val) => getUserInfoTypeName(val) },
     { field: 'userName', label: '操作员' },
     { field: 'modifytime', label: '最后更新时间', formatValue: (val) => formatDate(val) },
     { field: 'operate', label: '操作', width: 140, fixed: 'right' }

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

@@ -32,7 +32,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.AccountTransactionsReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.AccountTransactionsReq>()
@@ -111,6 +111,6 @@ const processRequiredParams = (callback: (params: Model.AccountTransactionsReq)
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value.filter((e) => ![15, 97, 99].includes(e.value))
+    filterOptons.selectList[0].options = getMarketOptions([15, 97, 99], true)
 })
 </script>

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

@@ -40,7 +40,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, getMarketById, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, getMarketById, registerMarketReadyCallback } = useMarket()
 // 委托类型
 const buildtypeEnum = useEnum('buildtype')
 // 委托状态
@@ -144,7 +144,7 @@ const processRequiredParams = (callback: (params: Model.CommissionSheetReq) => v
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value.filter((e) => ![15, 97, 99].includes(e.value))
+    filterOptons.selectList[0].options = getMarketOptions([15, 97, 99], true)
 })
 
 buildtypeEnum.registerEnumReadyCallback(() => {

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

@@ -39,7 +39,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.PositionSheetReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.PositionSheetReq>()
@@ -139,6 +139,6 @@ const processRequiredParams = (callback: (params: Model.ContractSheetReq) => voi
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value
+    filterOptons.selectList[0].options = getMarketOptions()
 })
 </script>

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

@@ -33,7 +33,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.PositionSumSheetReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.PositionSumSheetReq>()
@@ -110,6 +110,6 @@ const processRequiredParams = (callback: (params: Model.PositionSumSheetReq) =>
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value
+    filterOptons.selectList[0].options = getMarketOptions()
 })
 </script>

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

@@ -40,7 +40,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const buildtypeEnum = useEnum('buildtype') // 单据类型
 const queryParams = shallowRef<Model.ContractSheetReq>()
 
@@ -149,7 +149,7 @@ const processRequiredParams = (callback: (params: Model.ContractSheetReq) => voi
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value.filter((e) => ![15, 97, 99].includes(e.value))
+    filterOptons.selectList[0].options = getMarketOptions([15, 97, 99], true)
 })
 
 buildtypeEnum.registerEnumReadyCallback(() => {

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

@@ -32,7 +32,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.GoodsTradeReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.GoodsTradeReq>()
@@ -107,7 +107,7 @@ const processRequiredParams = (callback: (params: Model.GoodsTradeReq) => void,
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value.filter((e) => ![15, 97, 99].includes(e.value))
+    filterOptons.selectList[0].options = getMarketOptions([15, 97, 99], true)
 })
 
 </script>

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

@@ -45,7 +45,7 @@ const wrtradetype2Enum = useEnum('wrtradetype2')
 // 状态
 const wrTradeOrderStatusEnum = useEnum('WRTradeOrderStatus')
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.WarehouseQueryReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.WarehouseQueryReq>()
@@ -131,7 +131,7 @@ const processRequiredParams = (callback: (params: Model.WarehouseQueryReq) => vo
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value
+    filterOptons.selectList[0].options = getMarketOptions()
 })
 
 wrtradetype2Enum.registerEnumReadyCallback(() => {

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

@@ -39,7 +39,7 @@ import AppFilter from '@pc/components/base/table-filter/index.vue'
 import AppPagination from '@pc/components/base/pagination/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
 
-const { marketOptions, registerMarketReadyCallback } = useMarket()
+const { getMarketOptions, registerMarketReadyCallback } = useMarket()
 const queryParams = shallowRef<Model.TradeOrderReq>()
 
 const { filterOptons, getQueryParams } = useDataFilter<Model.TradeOrderReq>()
@@ -127,6 +127,6 @@ const processRequiredParams = (callback: (params: Model.WarehouseQueryReq) => vo
 }
 
 registerMarketReadyCallback(() => {
-    filterOptons.selectList[0].options = marketOptions.value
+    filterOptons.selectList[0].options = getMarketOptions()
 })
 </script>

+ 36 - 1
src/services/api/investor/index.ts

@@ -158,7 +158,7 @@ export function getTaaccountList(options: CommonFetchOptions<{ request: Model.Ta
 /**
  * 交易商管理-->个性化管理-->风控个性化-->新增/修改
  */
-export function addAccountRiskConfig(options: CommonFetchOptions<{ request: Partial<Model.AddAccountRiskConfigReq>; }>) {
+export function addAccountRiskConfig(options: CommonFetchOptions<{ request: Model.AddAccountRiskConfigReq; }>) {
     return httpClient.commonRequest('/investor/addAccountRiskConfig', 'post', options)
 }
 
@@ -167,4 +167,39 @@ export function addAccountRiskConfig(options: CommonFetchOptions<{ request: Part
  */
 export function deleteConfig(options: CommonFetchOptions<{ request: Model.DeleteConfigReq; }>) {
     return httpClient.commonRequest('/investor/deleteConfig', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商个性化-->获取树结构
+ */
+export function getInvestorTree(options: CommonFetchOptions<{ response: Model.InvestorTreeRsp[]; }>) {
+    return httpClient.commonRequest('/investor/getTree', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商个性化-->新增-->初始化交易商个性化信息
+ */
+export function initInvestorPerson(options: CommonFetchOptions<{ request: Model.InvestorPersonReq; response: Model.InvestorPersonRsp; }>) {
+    return httpClient.commonRequest('/investor/initInvestorPerson', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商个性化-->新增/修改
+ */
+export function traderPersonAdd(options: CommonFetchOptions<{ request: Partial<Model.TraderPersonAddReq>; }>) {
+    return httpClient.commonRequest('/investor/traderPersonAdd', 'post', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商个性化-->删除
+ */
+export function deleteTraderConfig(options: CommonFetchOptions<{ request: Partial<Model.DeleteTraderConfigReq>; }>) {
+    return httpClient.commonRequest('/investor/deleteTraderConfig', 'post', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商个性化-->详情
+ */
+export function tradeConfigView(options: CommonFetchOptions<{ request: Model.TradeConfigViewReq; response: Model.TradeConfigViewRsp; }>) {
+    return httpClient.commonRequest('/investor/tradeConfigView', 'get', options)
 }

+ 249 - 0
src/types/model/investor.d.ts

@@ -532,4 +532,253 @@ declare namespace Model {
     interface DeleteConfigReq {
         accountid: number;
     }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->获取树结构 请求 */
+    interface InvestorTreeRsp {
+        goodsid: number;
+        id: string;
+        level: number;
+        marketid: number;
+        name: string;
+        pid: string;
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->新增-->初始化交易商个性化信息 请求 */
+    interface InvestorPersonReq {
+        goodsid: number;
+        marketid: number;
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->新增-->初始化交易商个性化信息 响应 */
+    interface InvestorPersonRsp {
+        feesConfigVos: {
+            crithm: number;
+            cvalue: number;
+            drithm: number;
+            dvalue: number;
+            exchangevalue: number;
+            feealgorithm: number;
+            grithm: number;
+            gvalue: number;
+            membermaxvalue: number;
+            memberminvalue: number;
+            qtydecimalplace: number;
+            tradefeeid: number;
+            tradefeename: string;
+        }[];
+        goods: {
+            agreeunit: number; // 期货合约乘数
+            areauserid: number; // 所属机构
+            auditaccountid: number; // 审核操作员账号
+            audittime: string; // 审核时间
+            cancelaccountid: number; // 注销操作员账号
+            canceltime: string; // 注销时间
+            closepricemode: number; // 强平价格方式 - 1:市价 2:最新价 3:涨跌停(未实现)
+            closepriceparam: number; // 强平最新价浮动比例 - 方式为2时使用 (<1)买 上浮 (1+x)最新价卖 下浮 (1-x)最新价
+            createtime: string; // 创建时间
+            creatorid: number; // 创建人
+            currencyid: number; // 报价货币ID
+            cutmode: number; // 斩仓方式 - 1:不斩仓 2:自动斩仓 3:半自动斩仓
+            decimalplace: number; // 报价小数位
+            delistingdate: string; // 实际退市时间(状态:待退市, 预售一口价、预售大宗直接改为退市)
+            delistingmode: number; // 退市方式 - 1:退市平仓 2:退市交收 3:不处理(仅期权币币兑换) 4:退市发货(所有权)
+            deliveryflag: number; // 交割标志 - 0:不可交割 1:可交割
+            forceclosemode: number; // 定期强平方式 - 1:无 2:每日 3:每周 4:每月[收益权]
+            forceclosevalue: number; // 定期强平参数 [收益权]
+            goodscode: string; // 期货合约代码(内部)
+            goodscurrencyid: number; // 合约货币ID
+            goodsgroupid: number; // 期货品种ID
+            goodsid: number; // 期货合约ID(自增ID SEQ_GOODS)
+            goodsname: string; // 期货合约名称
+            goodsquotetype: number; // 合约报价类型: 1-直接报价 2-间接报价
+            goodsstatus: number; // 商品状态- 1:待审核 2:未上市 3:上市 4:已注销 5:审核拒绝 6:退市 7:待退市
+            goodstradetype: number; // 商品交易权限类型 - 1:可建可平 2:可建不可平 3:不可建可平 4:不可建不可平
+            goodunitid: number; // 报价单位ID
+            hasquoter: number; // 是否有报价商 - 0:无报价商 1:有报价商
+            hqprotecttime: number; // 行情保护时间(秒)
+            innerdealmode: number; // 内部成交方式[通道交易] - 1:净头寸 2:开平 3:平今
+            isbuylimited: number; // 是否限制建仓量 - 0:不限制 1:限制
+            isvisible: number; // 游客是否可见 - 0:不可见 1:可见
+            lasttradedate: string; // 最后交易日期
+            listingdate: string; // 交易开始日期
+            marginalgorithm: number; // 持仓定金方式(49) - 1:比率 2.固定(52)
+            marketid: number; // 所属市场ID
+            modifierid: number; // 修改人
+            modifystatus: number; // 变更状态 - 1:未变更 2:变更中 3:待审核 4:已审核
+            modifytime: string; // 修改时间
+            newtransferdepositratio: number; // 转让定金比例(新)(49*)
+            outerdealmode: number; // 外部成交方式[通道交易]- 1:净头寸 2:开平 3:平今
+            outgoodscode: string; // 期货合约代码(外部)
+            pictureurl: string; // 商品图片(逗号)
+            presaleflag: number; // 发售标识(49) - 1:未预售 2:预售完成 3:预售申请中 4:预售中
+            presaleprice: number; // 发售价订货价(49)
+            provideraccountid: number; // 发售方资金账户ID(49)供货商资金账户ID(50)
+            provideruserid: number; // 发售方用户ID(49)供货商(50)
+            qtydecimalplace: number; // 成交量小数位
+            quotegear: number; // 行情档位(1-10)
+            quoteminunit: number; // 行情最小变动单位 [整数,报价小数位一起使用]
+            quoteshowtype: number; // 行情报价类型: 1:成交价 2:买卖中间价 3:含买价 4:含卖价 做市模式固定为3:含买价
+            quoteshowtypeinterval: number; // 行情报价间隔时间(秒)(成交价时为0)
+            refgoodscode: string; // 参考商品代码
+            refgoodsid: number; // 参考商品ID
+            remark: string; // 备注
+            swapdays: number; // 掉期天数
+            transferdepositratio: number; // 转让定金比例(49)持仓定金(52)
+        }[];
+        markets: {
+            auctionwrtype: number; // 仓单类型(现货交易) - 1:无仓单 2:有仓单 3;有无仓单均可
+            canacceptquote: number; // 确认行权是否接收行情 - 0:不接受 1:接受 [可确认权的挂牌期权市场可配置]
+            cangoodsexercise: number; // [期权]是否可现货行权- 0:否 1:是 是否交收
+            cangoodsexercisetype: number; // 可现货行权期权类型 - 1:认购 2认沽 3:认购认沽 [CanGoodsExercise = 1时可设置]
+            canmanualquotestrike: number; // 是否可手动报行权价- 0:否 1:是 [期权]
+            canmutistage: number; // 是否可多段运行 – 0:不可 1:可 [挂牌期权]
+            canpreexercise: number; // [期权]是否可预申报- 0:否 1:是
+            clearinterval: number; // 待开市间隔(交易市场开盘前多久发市场待开市信号(单位分钟))
+            contracttmp: string; // 合同模板[荷兰式][竞价式][仓单贸易]
+            deliverymode: number; // 交收方式(50模式) 1:配对交收 2:强平日交收
+            exchareaid: number; // 所属交易所,可以没有
+            financemarketid: number; // 融资回购市场ID [仓单贸易]
+            forceclosemode: number; // 定期强平方式 - 1:到期结 2:日结 3:周结 4:月结 [收益权] 枚举-forceCloseMode
+            forceclosevalue: number; // 定期强平参数 [收益权](周结、月结)
+            goodstype: number; // 商品类型 - 1:交易商品 2:仓单商品
+            hasrebate: number; // 是否返利[竞价式] 0:不返,1:返利 – 根据系统参数088显示或隐藏
+            hastradecredit: number; // 是否交易授信[做市收益权] - 0:不授信 1:授信
+            haswr: number; // [竞拍]是否需要仓单 - 0:不需要 1:需要- 作废
+            isdeductmargin: number; // 竞拍违约是否扣除保证金[竞拍-降价式] - 0:不扣 1:扣除
+            isdeliverybuyerpayonline: number; // 交收买方是否线上支付(50模式) - 0:否 1:是
+            isdeliverysellercanapply: number; // 卖方是否可申请交收(50模式 - DeliveryMode = 2时) - 0:否 1:是
+            isrecordsource: number; // 是否记录成交源 - 0:不记录 1:记录 [所有权]
+            isreleasemargin: number; // 成交参与保证金是否释放[竞价式] - 0:不释放 1释放
+            isupdatereckonprice: number; // 输入结算价标识 - 0:系统生成 1:手工输入(自动) 2:手工输入(手动)
+            marginformula: number; // 持仓保证金公式 - 1:双边收 2:净头寸收 3:大小边差异收 4:大边收 5:卖持仓收(仅期权)
+            marginformula2: number; // 持仓保证金公式(仅受托竞价) - 1:双边收 2:净头寸收 3:大小边差异收 4:大边收 5:卖持仓收(仅期权)
+            marketid: number; // 市场ID正常5位,前三位固定:两位表示交易模式, 一位表示交易属性(1:收益权,2:所有权) 其它特殊市场:0-系统 1-交割服务 2-账户服务3-履约服务 4-仓单服务 5-积分服务 6-银行服务
+            marketname: string; // 市场名称
+            marketserviceid: number; // 市场服务ID
+            marketstatus: number; // 生效状态(ValidStatus枚举): 1:待生效 2:正常 3:注销
+            markettype: number; // 市场类型- 1:非交易服务 2:交易服务
+            matchermode: number; // 指定对手模式[仓单贸易模式专用] - 1:任意对手 - 作废
+            openmethod: number; // 开盘模式 - 0 自动 1手动
+            otcuserid: number; // 场外期权做市商[场外期权]
+            outersynctime: string; // 外部同步时间点(h24:mi:ss)[场外期权]
+            paylaterflag: number; // 是否支持后付 - 0:不支持 1;支持 - [挂牌点选 所有权、预售挂牌支持]
+            pendingflag: number; // 待开市时间标识[通道交易-对冲] - 0:当日 1:上日
+            pendingtime: string; // 待开市时间通道交易-对冲
+            performancetempid: number; // 履约计划模板ID - 作废
+            premiumquotemode: number; // 权利金报价方式 - 1:自动 2:手动
+            rebateratio: number; // 返利比率[竞价式]
+            reckonorder: number; // 结算顺序
+            reckonpricealgorithm: number; // 结算价算法: 1:最后多少笔成交价加权平均 2:最后多少秒成交价加权平均 3:全天加权平均 4:最后一口价 5.买一价 6.卖一价 7.买一卖一均价 8.外部结算价
+            reckonpriceparam: number; // 结算价参数
+            reckontime: string; // 结算时间通道交易-对冲
+            riskcontrolmode: number; // 风控方式(52模式) 1:按单风控 2:按账户风控
+            roleprioritytype: number; // 角色优先类型 - 1:无 2:报价商优先 3:非报价商优先 [16:挂牌点选]
+            selllistingauditflag: number; // 卖挂牌是否需要审核(仓单贸易) - 0:不需要 1:需要
+            trademarkettype: number; // 交易市场类型 - 1:合约市场 2:外部市场 3:仓单市场
+            trademode: number; // 交易模式 - 10:做市 13:竞价 15:通道交易 16:挂牌点选 17:仓单贸易 18:期权 19:竞拍-降价式 20:竞拍-竞价式 21:竞拍-大宗式 22:受托竞价
+            tradeproperty: number; // 交易属性 - 1:收益权(可做空) 2:所有权(不可做空) 3:期权 4:现货 5:参考行情 6:通道交易 7:币交易 8:场外期权
+            tradetype: number; // 下单方式[通道交易] - 1:直接转单 2:净头寸下单
+            updatereckonpriceinterval: number; // 输入结算价时长(分钟) [1:手工输入(自动)]
+        }[];
+        results: {
+            enumdicname: string;
+            enumitemname: string;
+            marginalgorithm: number;
+            marketmarginvalue: number;
+        }[];
+        rulesConfigVos: {
+            defaultvalue: number;
+            dvalue: number;
+            gvalue: number;
+            paramvalue: number;
+            qtydecimalplace: number;
+            regexpress: string;
+            remark: string;
+            ruleid: number;
+            rulename: string;
+        }[];
+        userGroups: {
+            areauserid: number; // 所属机构ID[AreaUserID = 1为平台对机构分组]
+            autoid: number; // 自增ID
+            createtime: string; // 创建时间
+            creatorid: number; // 创建人
+            customertype: number; // 风险率客户类别
+            groupname: string; // 用户分组名称
+        }[];
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->新增/修改 请求 */
+    interface TraderPersonAddReq {
+        feeList: {
+            feealgorithm: number; // 费用算法 - 1:比率 2:固定
+            paramid: number; // 个性化参数ID - 1. 参照交易模式规则费用对应-参考表 可个性化类型 2.保证金类保存交易所创建的投资者客户类别ID
+            paramvalue: number; // 参数值(保证金类此项为空)
+        }[];
+        feealgorithm?: number;
+        flag: number; // 新增0,修改1
+        goodsid: number;
+        marketid: number;
+        paramid?: number;
+        ruleList: {
+            paramid: number; // 个性化参数ID - 1. 参照交易模式规则费用对应-参考表 可个性化类型 2.保证金类保存交易所创建的投资者客户类别ID
+            paramvalue: number; // 参数值(保证金类此项为空)
+        }[];
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->删除 请求 */
+    interface DeleteTraderConfigReq {
+        goodsid: number;
+        marketid: number;
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->详情 请求 */
+    interface TradeConfigViewReq {
+        goodsid: number;
+        marketid: number;
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商个性化-->详情 请求 */
+    interface TradeConfigViewRsp {
+        autoid: number; // 自增ID
+        config: {
+            autoid: number; // 自增ID
+            createtime: string; // 创建时间
+            creatorid: number; // 创建人
+            feealgorithm: number; // 费用算法 - 1:比率 2:固定
+            goodsid: number; // 商品ID
+            goodsname: string;
+            marketid: number; // 市场ID
+            marketname: string;
+            memberuserid: number; // 所属会员ID
+            paramid: number; // 个性化参数ID - 1. 参照交易模式规则费用对应-参考表 可个性化类型 2.保证金类保存交易所创建的投资者客户类别ID
+            paramvalue: number; // 参数值(保证金类此项为空)
+            ruletype: number; // 参数类型 -1.保证金类 2.交易规则 3.交易费用
+            usergroupid: number; // 用户分组ID(0的为会员默认设置)
+        };
+        createtime: string; // 创建时间
+        creatorid: number; // 创建人
+        dfeeList: [];
+        druleList: [];
+        feeList: [];
+        feealgorithm: number; // 费用算法 - 1:比率 2:固定
+        goodsid: number; // 商品ID
+        goodsname: string;
+        groupname: string;
+        marginalgorithm: number;
+        marketid: number; // 市场ID
+        marketmarginvalue: number;
+        marketmarginvaluedisplay: string;
+        marketname: string;
+        memberuserid: number; // 所属会员ID
+        paramid: number; // 个性化参数ID - 1. 参照交易模式规则费用对应-参考表 可个性化类型 2.保证金类保存交易所创建的投资者客户类别ID
+        paramvalue: number; // 参数值(保证金类此项为空)
+        ruleList: [];
+        ruletype: number; // 参数类型 -1.保证金类 2.交易规则 3.交易费用
+        usergroupid: number; // 用户分组ID(0的为会员默认设置)
+    }
 }