li.shaoyi před 1 rokem
rodič
revize
58e574d1c5
22 změnil soubory, kde provedl 1510 přidání a 48 odebrání
  1. 28 0
      src/constants/common.ts
  2. 12 5
      src/packages/pc/components/modules/select-member/index.vue
  3. 50 0
      src/packages/pc/views/investor/custom/group/components/delete/index.vue
  4. 140 0
      src/packages/pc/views/investor/custom/group/components/edit/index.vue
  5. 50 0
      src/packages/pc/views/investor/custom/group/components/user/delete/index.vue
  6. 73 0
      src/packages/pc/views/investor/custom/group/components/user/edit/index.vue
  7. 102 0
      src/packages/pc/views/investor/custom/group/components/user/index.vue
  8. 75 1
      src/packages/pc/views/investor/custom/group/index.vue
  9. 50 0
      src/packages/pc/views/investor/custom/riskcfg/components/delete/index.vue
  10. 74 0
      src/packages/pc/views/investor/custom/riskcfg/components/details/index.vue
  11. 152 0
      src/packages/pc/views/investor/custom/riskcfg/components/edit/index.vue
  12. 77 1
      src/packages/pc/views/investor/custom/riskcfg/index.vue
  13. 74 1
      src/packages/pc/views/investor/manage/modification/index.vue
  14. 82 1
      src/packages/pc/views/investor/user/initreview/index.vue
  15. 38 4
      src/packages/pc/views/investor/user/open/components/details/index.vue
  16. 1 1
      src/packages/pc/views/investor/user/open/components/edit/index.vue
  17. 4 15
      src/packages/pc/views/investor/user/open/index.vue
  18. 82 1
      src/packages/pc/views/investor/user/rereview/index.vue
  19. 18 15
      src/packages/pc/views/profitshare/institution/config/index.vue
  20. 2 1
      src/packages/pc/views/profitshare/institution/group/components/member/index.vue
  21. 106 1
      src/services/api/investor/index.ts
  22. 220 1
      src/types/model/investor.d.ts

+ 28 - 0
src/constants/common.ts

@@ -0,0 +1,28 @@
+/**
+ * 确认状态
+ */
+export enum Confirmation {
+    Yes = 1, // 是
+    No = 0, // 否
+}
+
+/**
+ * 获取确认状态列表
+ * @returns 
+ */
+export function getConfirmationList() {
+    return [
+        { label: '是', value: Confirmation.Yes },
+        { label: '否', value: Confirmation.No }
+    ]
+}
+
+/**
+ * 获取确认状态名称
+ * @param value 
+ * @returns 
+ */
+export function getConfirmationName(value?: number) {
+    const item = getConfirmationList().find((e) => e.value === value)
+    return item?.label ?? value
+}

+ 12 - 5
src/packages/pc/components/modules/select-member/index.vue

@@ -1,8 +1,8 @@
 <template>
     <div class="el-form-item--col">
-        <el-select ref="selectRef" v-model="selectedValue" :empty-values="[null, undefined, 0]" :loading="loading"
-            :placeholder="placeholder" :remote-method="remoteMethod" @change="dataList = []" remote filterable
-            clearable>
+        <el-select ref="selectRef" v-model="selectedValue" :empty-values="[null, undefined, 0]" :disabled="disabled"
+            :loading="loading" :placeholder="placeholder" :remote-method="remoteMethod" @change="onChange" remote
+            filterable clearable>
             <el-option v-for="item in dataList" :key="item.userid" :label="item.accountname" :value="item.userid">
                 <div style="display: flex;justify-content: space-between;">
                     <span>{{ item.userid }}</span>
@@ -15,7 +15,7 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, computed } from 'vue'
+import { shallowRef, computed, toRaw } from 'vue'
 import { ElMessage } from 'element-plus'
 import { useRequest } from '@/hooks/request'
 import { queryOrganSelect } from '@/services/api/common'
@@ -23,13 +23,14 @@ import { queryOrganSelect } from '@/services/api/common'
 const props = defineProps({
     modelValue: [Number, String],
     usertype: String,
+    disabled: Boolean,
     placeholder: {
         type: String,
         default: '请输入'
     }
 })
 
-const emit = defineEmits(['update:modelValue'])
+const emit = defineEmits(['update:modelValue', 'change'])
 
 const selectRef = shallowRef()
 
@@ -72,6 +73,12 @@ const remoteMethod = (query: string) => {
         })
     }
 }
+
+const onChange = (id?: number) => {
+    const item = dataList.value.find((e) => e.userid === id)
+    dataList.value = []
+    emit('change', toRaw(item))
+}
 </script>
 
 <style lang="less">

+ 50 - 0
src/packages/pc/views/investor/custom/group/components/delete/index.vue

@@ -0,0 +1,50 @@
+<!-- 交易商管理-个性化管理-交易商分组管理-删除 -->
+<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 { delInvest } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.PersonalizedRsp>,
+        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
+    delInvest({
+        data: {
+            autoid: props.record.autoid
+        }
+    }).then(() => {
+        ElMessage.success('删除成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('删除失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 140 - 0
src/packages/pc/views/investor/custom/group/components/edit/index.vue

@@ -0,0 +1,140 @@
+<!-- 交易商管理-个性化管理-交易商分组管理-编辑 -->
+<template>
+    <app-drawer title="编辑" width="900" 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="areauserid" v-if="userStore.userInfo.releType !== 2">
+                    <app-select-member v-model="formData.areauserid" usertype="2" placeholder="代码或名称模糊匹配" />
+                </el-form-item>
+                <el-form-item label="名称" prop="groupname">
+                    <el-input v-model="formData.groupname" maxlength="50" placeholder="请输入" />
+                </el-form-item>
+                <el-form-item label="风险率类型" prop="customertype">
+                    <el-select v-model="formData.customertype" clearable>
+                        <template v-for="(item, index) in configs" :key="index">
+                            <el-option :label="customerTypeEnum.getEnumTypeName(item.customertype)"
+                                :value="item.customertype" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+            </fieldset>
+        </el-form>
+        <app-table-details title="风险率信息" :data="selectedItem" :label-width="160" :cell-props="detailProps"
+                :column="2" v-if="selectedItem" />
+        <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, reactive, PropType, computed, onMounted } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { getConfirmationName } from '@/constants/common'
+import { useEnum } from '@/hooks/enum'
+import { addPersonalized, getRiskRatioTypeForMarketer } from '@/services/api/investor'
+import { CellProp } from '@pc/components/base/table-details/types'
+import { useUserStore } from '@/stores'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppSelectMember from '@pc/components/modules/select-member/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.PersonalizedRsp>
+    }
+})
+
+// 风险客户类别
+const customerTypeEnum = useEnum('customerType')
+
+const userStore = useUserStore()
+const formRef = ref<FormInstance>()
+const show = ref(true)
+const refresh = ref(false)
+const loading = ref(false)
+const configs = reactive<Model.RiskRatioTypeForMarketerRsp[]>([])
+
+const formData = ref<Model.AddPersonalizedReq>({
+    groupname: ''
+})
+
+// 选中的风险率配置
+const selectedItem = computed(() => configs.find((e) => e.customertype === formData.value.customertype))
+
+customerTypeEnum.registerEnumReadyCallback(async () => {
+    const options = customerTypeEnum.getEnumOptions()
+    for (const item of options) {
+        const res = await getRiskRatioTypeForMarketer({
+            data: {
+                customertype: item.value
+            }
+        })
+        if (res.data) {
+            configs.push(res.data)
+        }
+    }
+})
+
+// 表单验证规则
+const formRules: FormRules = {
+    areauserid: [{ required: true }],
+    groupname: [{ required: true }],
+}
+
+const detailProps = computed<CellProp[]>(() => {
+    const { notesaferatio } = selectedItem.value ?? {}
+    return [
+        { prop: 'riskcontrolmode', label: '风控模式:', formatValue: (val) => val === 1 ? '交易商' : '做市会员' },
+        { prop: 'customertype', label: '客户类别:', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+        { prop: 'riskratiocalcmode', label: '风险率计算方式:', formatValue: (val) => val === 1 ? '占用/净值' : '净值/占用' },
+        { prop: 'notemarginriskratio', label: '提示保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'addmarginriskratio', label: '追加保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutriskratio', label: '斩仓风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutbackriskratio', label: '斩仓恢复风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'notesaferatio', label: '提示安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'addsaferatio', label: '追加安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'recoversaferatio', label: '恢复正常安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'cutsaferatio', label: '斩仓安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'isdefault', label: '是否默认:', formatValue: (val) => getConfirmationName(val) },
+        { prop: 'markets', label: '斩仓市场顺序:' },
+    ]
+})
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            loading.value = true
+            addPersonalized({
+                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 = {
+            autoid: record.autoid,
+            areauserid: record.areauserid,
+            customertype: record.customertype,
+            groupname: record.groupname,
+        }
+    }
+})
+</script>

+ 50 - 0
src/packages/pc/views/investor/custom/group/components/user/delete/index.vue

@@ -0,0 +1,50 @@
+<!-- 交易商管理-个性化管理-交易商分组管理-分组交易商-删除 -->
+<template>
+    <app-drawer title="提示" v-model:show="show" :loading="loading" :refresh="refresh">
+        <div class="g-text-message">确认将{{ selectedItem.userid }}移除分组吗?</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 { delInvestorForPer } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedItem: {
+        type: Object as PropType<Model.PersonalizedDetailRsp>,
+        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
+    delInvestorForPer({
+        data: {
+            userid: props.selectedItem.userid
+        }
+    }).then(() => {
+        ElMessage.success('删除成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('删除失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 73 - 0
src/packages/pc/views/investor/custom/group/components/user/edit/index.vue

@@ -0,0 +1,73 @@
+<!-- 交易商管理-个性化管理-交易商分组管理-分组交易商-编辑 -->
+<template>
+    <app-drawer title="编辑" v-model:show="show" :refresh="refresh" :loading="loading">
+        <el-transfer v-model="selectedValue" :data="dataList" :titles="['备选交易商', '已选交易商']"
+            :props="{ label: 'accountname', key: 'userid' }">
+            <template #default="{ option }">
+                {{ option.userid }} -- {{ option.accountname }}
+            </template>
+        </el-transfer>
+        <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 { useRequest } from '@/hooks/request'
+import { queryInvestorForPer, addInvestorForPer } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.PersonalizedRsp>,
+        required: true
+    }
+})
+
+const show = shallowRef(true)
+const refresh = shallowRef(false)
+const loading = shallowRef(false)
+const selectedValue = shallowRef([])
+
+const { dataList } = useRequest(queryInvestorForPer, {
+    params: {
+        usergroupid: props.record.autoid
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    loading.value = true
+    addInvestorForPer({
+        data: {
+            adduserids: selectedValue.value.join(','),
+            usergroupid: props.record.autoid
+        }
+    }).then(() => {
+        ElMessage.success('保存成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('保存失败:' + err)
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>
+
+<style lang="less">
+.el-transfer-panel {
+    --el-transfer-panel-width: 260px;
+    --el-transfer-panel-body-height: 400px;
+}
+</style>

+ 102 - 0
src/packages/pc/views/investor/custom/group/components/user/index.vue

@@ -0,0 +1,102 @@
+<!-- 交易商管理-个性化管理-交易商分组管理-分组交易商 -->
+<template>
+    <teleport to="#appPageTeleport">
+        <app-view>
+            <template #header>
+                <app-filter :options="filterOptons" />
+            </template>
+            <app-table :data="dataList" :columns="tableColumns">
+                <template #headerLeft>
+                    <span>组名:{{ record.groupname }}</span>
+                </template>
+                <template #headerRight>
+                    <div style="display: flex;">
+                        <el-button type="primary" @click="showComponent('Edit')">新增</el-button>
+                        <el-button @click="emit('closed')">关闭</el-button>
+                    </div>
+                </template>
+                <!-- 操作 -->
+                <template #operate="{ row }">
+                    <el-button size="small" icon="Delete" @click="showComponent('Delete', row)" circle plain />
+                </template>
+                <template #footer>
+                    <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                        @change="onSearch" />
+                </template>
+            </app-table>
+            <component ref="componentRef" :is="componentMap.get(componentId)" v-bind="{ record, selectedItem }"
+                @closed="closeComponent" v-if="componentId" />
+        </app-view>
+    </teleport>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, defineAsyncComponent } from 'vue'
+import { ElMessage } from 'element-plus'
+import { formatDate } from '@/filters'
+import { useDataFilter } from '@/hooks/datatable'
+import { useComponent } from '@/hooks/component'
+import { useRequest } from '@/hooks/request'
+import { queryPersonalizedDetail } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+import AppFilter from '@pc/components/base/table-filter/index.vue'
+import AppPagination from '@pc/components/base/pagination/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.PersonalizedRsp>,
+        required: true
+    }
+})
+
+const componentMap = new Map<string, unknown>([
+    ['Edit', defineAsyncComponent(() => import('./edit/index.vue'))], // 编辑
+    ['Delete', defineAsyncComponent(() => import('./delete/index.vue'))], // 删除
+])
+
+const emit = defineEmits(['closed'])
+const selectedItem = ref<Model.PersonalizedDetailRsp>()
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.PersonalizedDetailReq>()
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => run())
+
+const { dataList, total, pageSize, pageIndex, run } = useRequest(queryPersonalizedDetail, {
+    params: {
+        pageNum: 1,
+        pageSize: 20,
+        usergroupid: props.record.autoid
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = ref<Model.TableColumn[]>([
+    { field: 'userid', label: '交易商代码' },
+    { field: 'accountname', label: '交易商名称' },
+    { field: 'createtime', label: '创建时间', formatValue: (val) => formatDate(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '交易商', keys: ['accountname'] },
+    { label: '手机号', keys: ['mobilephone'] },
+    { label: '证件号码', keys: ['cardnum'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
+
+const showComponent = (code: string, row?: Model.PersonalizedDetailRsp) => {
+    selectedItem.value = row
+    openComponent(code)
+}
+</script>

+ 75 - 1
src/packages/pc/views/investor/custom/group/index.vue

@@ -1,7 +1,81 @@
 <!-- 交易商管理-个性化管理-交易商分组管理 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
+            <template #headerLeft>
+                <app-operation :data-list="getFilteredButtons(['investor_custom_group_add'])" @click="openComponent" />
+            </template>
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small"
+                    :data-list="getFilteredButtons(['investor_custom_group_user', 'investor_custom_group_modify', 'investor_custom_group_delete'])"
+                    @click="(code: string) => openComponent(code, row)" circle />
+            </template>
+            <template #footer>
+                <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                    @change="onSearch" />
+            </template>
+        </app-table>
+        <component :is="componentMap.get(componentId)" v-bind="{ record }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ElMessage } from 'element-plus'
+import { formatDate } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { queryPersonalized } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+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 customerTypeEnum = useEnum('customerType')
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.PersonalizedReq>()
+
+const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.PersonalizedRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryPersonalized, {
+    params: {
+        pageNum: 1,
+        pageSize: 20
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'areausername', label: '会员名称' },
+    { field: 'groupname', label: '分组名称' },
+    { field: 'customertype', label: '风险客户类别', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+    { field: 'createtime', label: '创建时间', formatValue: (val) => formatDate(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '组名', keys: ['groupname'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
 </script>

+ 50 - 0
src/packages/pc/views/investor/custom/riskcfg/components/delete/index.vue

@@ -0,0 +1,50 @@
+<!-- 交易商管理-个性化管理-风控个性化-删除 -->
+<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 { deleteConfig } from '@/services/api/investor'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.AccountRiskConfigRsp>,
+        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
+    deleteConfig({
+        data: {
+            accountid: props.record.accountid
+        }
+    }).then(() => {
+        ElMessage.success('删除成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('删除失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 74 - 0
src/packages/pc/views/investor/custom/riskcfg/components/details/index.vue

@@ -0,0 +1,74 @@
+<!-- 交易商管理-个性化管理-风控个性化-详情 -->
+<template>
+    <app-drawer title="编辑" width="900" v-model:show="show" :refresh="refresh">
+        <app-table-details title="基本信息设置" :data="record" :label-width="160" :cell-props="detailProps1" :column="2" />
+        <app-table-details title="风险率信息" :data="data" :label-width="160" :cell-props="detailProps2" :column="2" />
+        <template #footer>
+            <el-button @click="onCancel(false)">关闭</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, computed } from 'vue'
+import { ElMessage } from 'element-plus'
+import { getConfirmationName } from '@/constants/common'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { getRiskRatioTypeForMarketer } from '@/services/api/investor'
+import { CellProp } from '@pc/components/base/table-details/types'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.AccountRiskConfigRsp>,
+        required: true
+    }
+})
+
+// 风险客户类别
+const customerTypeEnum = useEnum('customerType')
+
+const show = ref(true)
+const refresh = ref(false)
+
+const { data } = useRequest(getRiskRatioTypeForMarketer, {
+    params: {
+        customertype: props.record.customertype
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const detailProps1: CellProp[] = [
+    { prop: 'username', label: '交易商:', },
+    { prop: 'accountid', label: '资金账户:', },
+    { prop: 'customertype', label: '风险率类型:', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+]
+
+const detailProps2 = computed<CellProp[]>(() => {
+    const { notesaferatio } = data.value ?? {}
+    return [
+        { prop: 'riskcontrolmode', label: '风控模式:', formatValue: (val) => val === 1 ? '交易商' : '做市会员' },
+        { prop: 'customertype', label: '客户类别:', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+        { prop: 'riskratiocalcmode', label: '风险率计算方式:', formatValue: (val) => val === 1 ? '占用/净值' : '净值/占用' },
+        { prop: 'notemarginriskratio', label: '提示保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'addmarginriskratio', label: '追加保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutriskratio', label: '斩仓风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutbackriskratio', label: '斩仓恢复风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'notesaferatio', label: '提示安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'addsaferatio', label: '追加安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'recoversaferatio', label: '恢复正常安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'cutsaferatio', label: '斩仓安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'isdefault', label: '是否默认:', formatValue: (val) => getConfirmationName(val) },
+        { prop: 'markets', label: '斩仓市场顺序:' },
+    ]
+})
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+</script>

+ 152 - 0
src/packages/pc/views/investor/custom/riskcfg/components/edit/index.vue

@@ -0,0 +1,152 @@
+<!-- 交易商管理-个性化管理-风控个性化-编辑 -->
+<template>
+    <app-drawer title="编辑" width="900" 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="userid">
+                    <app-select-member v-model="formData.userid" usertype="5" placeholder="代码或名称模糊匹配"
+                        :disabled="!!record" @change="onMemberChange" />
+                </el-form-item>
+                <el-form-item label="资金账户" prop="accountid">
+                    <el-select v-model="formData.accountid" :disabled="!!record">
+                        <template v-for="(value, index) in taaccountList" :key="index">
+                            <el-option :label="value" :value="value" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="风险率类型" prop="customertype">
+                    <el-select v-model="formData.customertype">
+                        <template v-for="(item, index) in configs" :key="index">
+                            <el-option :label="customerTypeEnum.getEnumTypeName(item.customertype)"
+                                :value="item.customertype" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+            </fieldset>
+        </el-form>
+        <app-table-details title="风险率信息" :data="selectedItem" :label-width="160" :cell-props="detailProps" :column="2"
+            v-if="selectedItem" />
+        <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, reactive, PropType, computed, onMounted } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { getConfirmationName } from '@/constants/common'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { addAccountRiskConfig, getRiskRatioTypeForMarketer, getTaaccountList } from '@/services/api/investor'
+import { CellProp } from '@pc/components/base/table-details/types'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppSelectMember from '@pc/components/modules/select-member/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Model.AccountRiskConfigRsp>
+    }
+})
+
+// 风险客户类别
+const customerTypeEnum = useEnum('customerType')
+
+const formRef = ref<FormInstance>()
+const show = ref(true)
+const refresh = ref(false)
+const loading = ref(false)
+const configs = reactive<Model.RiskRatioTypeForMarketerRsp[]>([])
+
+const formData = ref<Partial<Model.AddAccountRiskConfigReq>>({})
+
+// 选中的风险率配置
+const selectedItem = computed(() => configs.find((e) => e.customertype === formData.value.customertype))
+
+customerTypeEnum.registerEnumReadyCallback(async () => {
+    const options = customerTypeEnum.getEnumOptions()
+    for (const item of options) {
+        const res = await getRiskRatioTypeForMarketer({
+            data: {
+                customertype: item.value
+            }
+        })
+        if (res.data) {
+            configs.push(res.data)
+        }
+    }
+})
+
+const { dataList: taaccountList, run } = useRequest(getTaaccountList, {
+    manual: true,
+})
+
+const onMemberChange = (item?: Model.OrganSelectRsp) => {
+    taaccountList.value = []
+    formData.value.accountid = undefined
+    if (item) run({ userid: item.userid })
+}
+
+// 表单验证规则
+const formRules: FormRules = {
+    userid: [{ required: true }],
+    accountid: [{ required: true }],
+    customertype: [{ required: true }],
+}
+
+const detailProps = computed<CellProp[]>(() => {
+    const { notesaferatio } = selectedItem.value ?? {}
+    return [
+        { prop: 'riskcontrolmode', label: '风控模式:', formatValue: (val) => val === 1 ? '交易商' : '做市会员' },
+        { prop: 'customertype', label: '客户类别:', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+        { prop: 'riskratiocalcmode', label: '风险率计算方式:', formatValue: (val) => val === 1 ? '占用/净值' : '净值/占用' },
+        { prop: 'notemarginriskratio', label: '提示保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'addmarginriskratio', label: '追加保证金风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutriskratio', label: '斩仓风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'cutbackriskratio', label: '斩仓恢复风险率:', formatValue: (val) => val + '%', show: !notesaferatio },
+        { prop: 'notesaferatio', label: '提示安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'addsaferatio', label: '追加安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'recoversaferatio', label: '恢复正常安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'cutsaferatio', label: '斩仓安全度:', formatValue: (val) => val + '%', show: !!notesaferatio },
+        { prop: 'isdefault', label: '是否默认:', formatValue: (val) => getConfirmationName(val) },
+        { prop: 'markets', label: '斩仓市场顺序:' },
+    ]
+})
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            loading.value = true
+            addAccountRiskConfig({
+                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>

+ 77 - 1
src/packages/pc/views/investor/custom/riskcfg/index.vue

@@ -1,7 +1,83 @@
 <!-- 交易商管理-个性化管理-风控个性化 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
+            <template #headerLeft>
+                <app-operation :data-list="getFilteredButtons(['investor_custom_riskcfg_add'])"
+                    @click="openComponent" />
+            </template>
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small"
+                    :data-list="getFilteredButtons(['investor_custom_riskcfg_details', 'investor_custom_riskcfg_modify', 'investor_custom_riskcfg_delete'])"
+                    @click="(code: string) => openComponent(code, row)" circle />
+            </template>
+            <template #footer>
+                <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                    @change="onSearch" />
+            </template>
+        </app-table>
+        <component :is="componentMap.get(componentId)" v-bind="{ record }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ElMessage } from 'element-plus'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { queryAccountRiskConfig } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+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 customerTypeEnum = useEnum('customerType')
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.AccountRiskConfigReq>()
+
+const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.AccountRiskConfigRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryAccountRiskConfig, {
+    params: {
+        pageNum: 1,
+        pageSize: 20
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'memberusername', label: '所属会员' },
+    { field: 'userid', label: '交易商代码' },
+    { field: 'username', label: '交易商名称' },
+    { field: 'accountid', label: '资金账户' },
+    { field: 'customertype', label: '风险率类型(客户类别)', formatValue: (val) => customerTypeEnum.getEnumTypeName(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '交易商', keys: ['username'] },
+    { label: '资金账户', keys: ['accountid'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
 </script>

+ 74 - 1
src/packages/pc/views/investor/manage/modification/index.vue

@@ -1,7 +1,80 @@
 <!-- 交易商管理-交易商管理-交易商变更审核 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small" :data-list="getFilteredButtons(['investor_manage_modification_audit'])"
+                    @click="(code: string) => openComponent(code, row)" circle />
+            </template>
+            <template #footer>
+                <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                    @change="onSearch" />
+            </template>
+        </app-table>
+        <component :is="componentMap.get(componentId)" v-bind="{ record }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ElMessage } from 'element-plus'
+import { formatDate } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { queryModifyPage } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+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 modifystatusEnum = useEnum('modifystatus')
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.ModifyPageReq>()
+
+const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.ModifyPageRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryModifyPage, {
+    params: {
+        pageNum: 1,
+        pageSize: 20
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'userid', label: '交易商代码' },
+    { field: 'accountnamedisplay', label: '交易商' },
+    { field: 'memberusername', label: '所属会员' },
+    { field: 'parentname', label: '所属机构' },
+    { field: 'modifystatus', label: '变更状态', formatValue: (val) => modifystatusEnum.getEnumTypeName(val) },
+    { field: 'audittime', label: '最后更新时间', formatValue: (val) => formatDate(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '所属会员\\机构', keys: ['parentname'] },
+    { label: '账户', keys: ['accountname'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
 </script>

+ 82 - 1
src/packages/pc/views/investor/user/initreview/index.vue

@@ -1,7 +1,88 @@
 <!-- 交易商管理-开户管理-开户初审 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
+            <template #headerLeft>
+                <app-operation :data-list="getFilteredButtons(['investor_user_initreview_batch'])"
+                    @click="openComponent" />
+            </template>
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small" :data-list="getFilteredButtons(['investor_user_initreview_audit'])"
+                    @click="(code: string) => openComponent(code, row)" circle />
+            </template>
+            <template #footer>
+                <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                    @change="onSearch" />
+            </template>
+        </app-table>
+        <component :is="componentMap.get(componentId)" v-bind="{ record, audit: true }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ElMessage } from 'element-plus'
+import { formatDate } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { queryInvestor } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+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 openmodeEnum = useEnum('openmode')
+// 开户状态
+const userstateEnum = useEnum('investorOpenStatus')
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.InvestorReq>()
+
+const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.InvestorRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryInvestor, {
+    params: {
+        pageNum: 1,
+        pageSize: 20,
+        show: 'firstaudit',
+        userState: 2
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'userName', label: '用户名称' },
+    { field: 'memberUserName', label: '所属会员' },
+    { field: 'areaName', label: '所属机构' },
+    { field: 'openMode', label: '开户方式', formatValue: (val) => openmodeEnum.getEnumTypeName(val) },
+    { field: 'userState', label: '开户状态', formatValue: (val) => userstateEnum.getEnumTypeName(val) },
+    { field: 'createTime', label: '申请时间', formatValue: (val) => formatDate(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '所属会员\\机构', keys: ['areaCode'] },
+    { label: '用户名', keys: ['userName'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
 </script>

+ 38 - 4
src/packages/pc/views/investor/user/open/components/details/index.vue

@@ -1,6 +1,6 @@
 <!-- 交易商管理-开户管理-交易商开户-详情 -->
 <template>
-    <app-drawer title="详情" width="900" v-model:show="show">
+    <app-drawer title="详情" width="900" v-model:show="show" :refresh="refresh">
         <app-table-details :data="data" :label-width="140" :cell-props="detailProps" :column="2">
             <!-- 证件照正面 -->
             <template #cardfrontphotourl="{ value }">
@@ -33,18 +33,22 @@
         </app-table-details>
         <template #footer>
             <el-button @click="onCancel(false)">关闭</el-button>
+            <template v-if="audit">
+                <el-button type="primary" @click="onAudit(1)">审核通过</el-button>
+                <el-button type="primary" @click="onAudit(0)">审核不通过</el-button>
+            </template>
         </template>
     </app-drawer>
 </template>
 
 <script lang="ts" setup>
 import { shallowRef, PropType, computed } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { decryptAES } from '@/services/crypto'
 import { getUserInfoTypeName, getGenderName, UserInfoType } from '@/constants/member'
 import { useEnum } from '@/hooks/enum'
 import { useRequest } from '@/hooks/request'
-import { queryInvestorDetail } from '@/services/api/investor'
+import { queryInvestorDetail, investorProcess } from '@/services/api/investor'
 import { CellProp } from '@pc/components/base/table-details/types'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppTableDetails from '@pc/components/base/table-details/index.vue'
@@ -54,7 +58,8 @@ const props = defineProps({
     record: {
         type: Object as PropType<Model.InvestorRsp>,
         required: true
-    }
+    },
+    audit: Boolean
 })
 
 const show = shallowRef(true)
@@ -127,6 +132,35 @@ const detailProps = computed(() => {
     return result
 })
 
+const onAudit = (auditflag: number) => {
+    ElMessageBox({
+        title: '提示',
+        message: '确认提交审核吗?',
+        showCancelButton: true,
+        beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+                instance.confirmButtonLoading = true
+                investorProcess({
+                    data: {
+                        auditflag,
+                        auditid: props.record.userId,
+                        msg: ''
+                    }
+                }).then(() => {
+                    ElMessage.success('审核成功')
+                    onCancel(true)
+                }).catch((err) => {
+                    ElMessage.error('审核失败:' + err)
+                }).finally(() => {
+                    done()
+                })
+            } else {
+                done()
+            }
+        }
+    })
+}
+
 const onCancel = (isRefresh = false) => {
     show.value = false
     refresh.value = isRefresh

+ 1 - 1
src/packages/pc/views/investor/user/open/components/edit/index.vue

@@ -122,7 +122,7 @@
             </el-form-item>
         </el-form>
         <template #footer>
-            <el-button @click="onCancel(false)">取消</el-button>
+            <el-button @click="onCancel(false)">关闭</el-button>
             <el-button type="primary" @click="onSubmit">提交审核</el-button>
         </template>
     </app-drawer>

+ 4 - 15
src/packages/pc/views/investor/user/open/index.vue

@@ -8,18 +8,6 @@
             <template #headerLeft>
                 <app-operation :data-list="getFilteredButtons(['investor_user_open_add'])" @click="openComponent" />
             </template>
-            <!-- 开户方式 -->
-            <template #openMode="{ value }">
-                {{ openmodeEnum.getEnumTypeName(value) }}
-            </template>
-            <!-- 开户状态 -->
-            <template #userState="{ value }">
-                {{ userstateEnum.getEnumTypeName(value) }}
-            </template>
-            <!-- 申请时间 -->
-            <template #createTime="{ value }">
-                {{ formatDate(value) }}
-            </template>
             <!-- 操作 -->
             <template #operate="{ row }">
                 <app-operation size="small" :data-list="handleOperateButtons(row)"
@@ -75,9 +63,9 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'userName', label: '用户名称' },
     { field: 'memberUserName', label: '所属会员' },
     { field: 'areaName', label: '所属机构' },
-    { field: 'openMode', label: '开户方式' },
-    { field: 'userState', label: '开户状态' },
-    { field: 'createTime', label: '申请时间' },
+    { field: 'openMode', label: '开户方式', formatValue: (val) => openmodeEnum.getEnumTypeName(val) },
+    { field: 'userState', label: '开户状态', formatValue: (val) => userstateEnum.getEnumTypeName(val) },
+    { field: 'createTime', label: '申请时间', formatValue: (val) => formatDate(val) },
     { field: 'operate', label: '操作', fixed: 'right' }
 ])
 
@@ -96,6 +84,7 @@ const handleOperateButtons = (row: Model.InvestorRsp) => {
     switch (row.userState) {
         case 1:
         case 3:
+        case 5:
             return getFilteredButtons(['investor_user_open_details', 'investor_user_open_modify', 'investor_user_open_delete'])
         case 2:
             return getFilteredButtons(['investor_user_open_details', 'investor_user_open_cancel'])

+ 82 - 1
src/packages/pc/views/investor/user/rereview/index.vue

@@ -1,7 +1,88 @@
 <!-- 交易商管理-开户管理-开户复审 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
+            <template #headerLeft>
+                <app-operation :data-list="getFilteredButtons(['investor_user_rereview_batch'])"
+                    @click="openComponent" />
+            </template>
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small" :data-list="getFilteredButtons(['investor_user_rereview_audit'])"
+                    @click="(code: string) => openComponent(code, row)" circle />
+            </template>
+            <template #footer>
+                <app-pagination :total="total" v-model:page-size="pageSize" v-model:page-index="pageIndex"
+                    @change="onSearch" />
+            </template>
+        </app-table>
+        <component :is="componentMap.get(componentId)" v-bind="{ record, audit: true }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { ElMessage } from 'element-plus'
+import { formatDate } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { queryInvestor } from '@/services/api/investor'
+import AppTable from '@pc/components/base/table/index.vue'
+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 openmodeEnum = useEnum('openmode')
+// 开户状态
+const userstateEnum = useEnum('investorOpenStatus')
+
+const { filterOptons, getQueryParams } = useDataFilter<Model.InvestorReq>()
+
+const { componentMap, componentId, record, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.InvestorRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(queryInvestor, {
+    params: {
+        pageNum: 1,
+        pageSize: 20,
+        show: 'audit',
+        userState: 4
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'userName', label: '用户名称' },
+    { field: 'memberUserName', label: '所属会员' },
+    { field: 'areaName', label: '所属机构' },
+    { field: 'openMode', label: '开户方式', formatValue: (val) => openmodeEnum.getEnumTypeName(val) },
+    { field: 'userState', label: '开户状态', formatValue: (val) => userstateEnum.getEnumTypeName(val) },
+    { field: 'createTime', label: '申请时间', formatValue: (val) => formatDate(val) },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '所属会员\\机构', keys: ['areaCode'] },
+    { label: '用户名', keys: ['userName'] }
+]
+
+filterOptons.buttonList = [
+    { lable: '查询', className: 'el-button--primary', onClick: () => onSearch() },
+    { lable: '重置', className: 'el-button--primary', onClick: () => onSearch(true) }
+]
+
+const onSearch = (clear = false) => {
+    const qs = getQueryParams(clear)
+    run(qs)
+}
 </script>

+ 18 - 15
src/packages/pc/views/profitshare/institution/config/index.vue

@@ -1,21 +1,24 @@
 <!-- 分润管理-机构分润配置-子机构分润配置 -->
 <template>
     <app-view>
-        <fieldset v-for="(item, index) in dataList" :key="index">
-            <legend>{{ item.feeName + (item.marketId && ` [${item.marketName}]`) }}</legend>
-            <app-table :data="item.configs" :columns="tableColumns">
-                <template #headerLeft>
-                    <app-operation :data-list="getFilteredButtons(['profitshare_institution_config_add'])"
-                        @click="(code: string) => showComponent(code, item)" />
-                </template>
-                <!-- 操作 -->
-                <template #operate="{ row }">
-                    <app-operation size="small"
-                        :data-list="getFilteredButtons(['profitshare_institution_config_modify', 'profitshare_institution_config_delete'])"
-                        @click="(code: string) => showComponent(code, item, row)" circle />
-                </template>
-            </app-table>
-        </fieldset>
+        <template v-if="dataList.length">
+            <fieldset v-for="(item, index) in dataList" :key="index">
+                <legend>{{ item.feeName + (item.marketId && ` [${item.marketName}]`) }}</legend>
+                <app-table :data="item.configs" :columns="tableColumns">
+                    <template #headerLeft>
+                        <app-operation :data-list="getFilteredButtons(['profitshare_institution_config_add'])"
+                            @click="(code: string) => showComponent(code, item)" />
+                    </template>
+                    <!-- 操作 -->
+                    <template #operate="{ row }">
+                        <app-operation size="small"
+                            :data-list="getFilteredButtons(['profitshare_institution_config_modify', 'profitshare_institution_config_delete'])"
+                            @click="(code: string) => showComponent(code, item, row)" circle />
+                    </template>
+                </app-table>
+            </fieldset>
+        </template>
+        <el-empty v-else />
         <component :is="componentMap.get(componentId)" v-bind="{ config, record }" @closed="closeComponent"
             v-if="componentId" />
     </app-view>

+ 2 - 1
src/packages/pc/views/profitshare/institution/group/components/member/index.vue

@@ -112,11 +112,12 @@ const onDelete = (row: Model.ProfitShareMemberQueryRsp) => {
                     }
                 }).then(() => {
                     ElMessage.success('删除成功')
-                    done()
                     run()
                     getUserList()
                 }).catch((err) => {
                     ElMessage.error('删除失败:' + err)
+                }).finally(() => {
+                    done()
                 })
             } else {
                 done()

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

@@ -37,6 +37,13 @@ export function queryInvestorListDetail(options: CommonFetchOptions<{ request: M
 }
 
 /**
+ * 交易商管理-->开户管理-->交易商初审/复审
+ */
+export function investorProcess(options: CommonFetchOptions<{ request: Model.InvestorProcessReq; }>) {
+    return httpClient.commonRequest('/investor/investorProcess', 'get', options)
+}
+
+/**
  * 交易商管理导出
  */
 export function investorExport(options: CommonFetchOptions<{ response: string; }> = {}) {
@@ -60,6 +67,104 @@ export function deleteAccount(options: CommonFetchOptions<{ request: Model.Delet
 /**
  * 交易商管理-->交易商管理-->资金账户、资金账户详情(userid不传)
  */
-export function showAccount(options: CommonFetchOptions<{ request: Model.ShowAccountReq; response:  Model.ShowAccountRsp[]; }> = {}) {
+export function showAccount(options: CommonFetchOptions<{ request: Model.ShowAccountReq; response: Model.ShowAccountRsp[]; }> = {}) {
     return httpClient.commonRequest('/investor/showAccount', 'get', options)
 }
+
+/**
+ * 交易商管理-->交易商变更审核-->查询交易商变更审核列表
+ */
+export function queryModifyPage(options: CommonFetchOptions<{ request: Model.ModifyPageReq; response: Model.ModifyPageRsp[]; }>) {
+    return httpClient.commonRequest('/investor/queryModifyPage', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->获取列表
+ */
+export function queryPersonalized(options: CommonFetchOptions<{ request: Model.PersonalizedReq; response: Model.PersonalizedRsp[]; }>) {
+    return httpClient.commonRequest('/investor/queryPersonalized', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表
+ */
+export function queryPersonalizedDetail(options: CommonFetchOptions<{ request: Model.PersonalizedDetailReq; response: Model.PersonalizedDetailRsp[]; }>) {
+    return httpClient.commonRequest('/investor/queryPersonalizedDetail', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->初始化交易商
+ */
+export function queryInvestorForPer(options: CommonFetchOptions<{ request: Model.InvestorForPerReq; response: Model.InvestorForPerRsp; }>) {
+    return httpClient.commonRequest('/investor/queryInvestorForPer', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->获取所选客户信息对应风险率
+ */
+export function getRiskRatioTypeForMarketer(options: CommonFetchOptions<{ request: Model.RiskRatioTypeForMarketerReq; response: Model.RiskRatioTypeForMarketerRsp; }>) {
+    return httpClient.commonRequest('/investor/getRiskRatioTypeForMarketer', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->新增
+ */
+export function addPersonalized(options: CommonFetchOptions<{ request: Model.AddPersonalizedReq; }>) {
+    return httpClient.commonRequest('/investor/addPersonalized', 'post', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->删除
+ */
+export function delInvest(options: CommonFetchOptions<{ request: Model.DelInvestReq; }>) {
+    return httpClient.commonRequest('/investor/delInvest', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->新增
+ */
+export function addInvestorForPer(options: CommonFetchOptions<{ request: Model.AddInvestorForPerReq; }>) {
+    return httpClient.commonRequest('/investor/addInvestorForPer', 'post', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->删除
+ */
+export function delInvestorForPer(options: CommonFetchOptions<{ request: Model.DelInvestorForPerReq; }>) {
+    return httpClient.commonRequest('/investor/delInvestorForPer', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->风控个性化-->获取列表
+ */
+export function queryAccountRiskConfig(options: CommonFetchOptions<{ request: Model.AccountRiskConfigReq; response: Model.AccountRiskConfigRsp[]; }>) {
+    return httpClient.commonRequest('/investor/queryAccountRiskConfig', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->风控个性化-->详情
+ */
+export function investorView(options: CommonFetchOptions<{ request: Model.InvestorViewReq; response: Model.InvestorViewRsp; }>) {
+    return httpClient.commonRequest('/investor/view', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->风控个性化-->新增获取交易商资金账户信息
+ */
+export function getTaaccountList(options: CommonFetchOptions<{ request: Model.TaaccountListReq; response: number[]; }>) {
+    return httpClient.commonRequest('/investor/getTaaccountList', 'get', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->风控个性化-->新增/修改
+ */
+export function addAccountRiskConfig(options: CommonFetchOptions<{ request: Partial<Model.AddAccountRiskConfigReq>; }>) {
+    return httpClient.commonRequest('/investor/addAccountRiskConfig', 'post', options)
+}
+
+/**
+ * 交易商管理-->个性化管理-->风控个性化-->删除
+ */
+export function deleteConfig(options: CommonFetchOptions<{ request: Model.DeleteConfigReq; }>) {
+    return httpClient.commonRequest('/investor/deleteConfig', 'get', options)
+}

+ 220 - 1
src/types/model/investor.d.ts

@@ -128,6 +128,15 @@ declare namespace Model {
         };
     }
 
+    /** 交易商管理-->开户管理-->交易商初审/复审 请求 */
+    interface InvestorProcessReq {
+        auditflag: number; // 审核标志 0拒绝 1通过
+        auditid: number; // 当前开户id
+        hasauth?: number;
+        msg: string; // 不通过原因
+        userType?: number;
+    }
+
     /** 交易商管理-->交易商管理-->查询个性化设置 请求 */
     interface AccountQueryReq {
         // accountid
@@ -169,7 +178,7 @@ declare namespace Model {
         // loginid
         loginid?: number;
     }
-    
+
     /** 交易商管理-->交易商管理-->资金账户、资金账户详情(userid不传) 请求 */
     interface ShowAccountReq {
         // accountid
@@ -311,6 +320,216 @@ declare namespace Model {
         usedmargin: number
         // 用户ID
         userid: number
+    }
+
+    /** 交易商管理-->交易商变更审核-->查询交易商变更审核列表 请求 */
+    interface ModifyPageReq {
+        accountname?: string; // 账户
+        memberuserid?: number;
+        pageNum: number;
+        pageSize: number;
+        parentname?: string;
+    }
+
+    /** 交易商管理-->交易商变更审核-->查询交易商变更审核列表 响应 */
+    interface ModifyPageRsp {
+        accountnamedisplay: string; // 交易商
+        audittime: string; // 最后更新时间
+        memberusername: string; // 所属会员
+        modifystatus: number; // 变更状态
+        parentname: string; // 所属机构
+        userid: number; // 交易商代码
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->获取列表 请求 */
+    interface PersonalizedReq {
+        groupname?: string;
+        pageNum: number;
+        pageSize: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->获取列表 响应 */
+    interface PersonalizedRsp {
+        areauserid: number;
+        areausername: string; // 会员名称
+        autoid: number; // id
+        createtime: string; // 创建时间
+        customertype: number; // 风险客户类别
+        groupname: string; // 分组名称
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表 请求 */
+    interface PersonalizedDetailReq {
+        accountname?: string; // 交易商
+        cardnum?: string; // 证件号码
+        mobilephone?: string; // 手机号
+        pageNum: number; // 页码
+        pageSize: number; // 页大小
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表 响应 */
+    interface PersonalizedDetailRsp {
+        accountname: string; // 交易商名称
+        createtime: string; // 创建时间
+        userid: number; // 交易商代码
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->初始化交易商 请求 */
+    interface InvestorForPerReq {
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->初始化交易商 响应 */
+    interface InvestorForPerRsp {
+        accountname: string; // 账户名称(机构名称)
+        accountstatus: number; // code areastatus 账户状态 - 1:待激活 2:待审核 3:待复审 4:正常 5:审核拒绝 6:停用(注销) 7:注销(删除)
+        applysrc: number; // 申请来源 - 1:管理端 2:终端
+        auditremark: string; // 审核备注
+        auditsrc: number; // 审核来源 - 1:管理端 2:终端
+        audittime: string; // 审核时间
+        audituserid: number; // 审核人
+        broker: number; // 所属经纪人ID(所属客户经理-千海金)
+        canceltime: string; // 销户时间
+        canceluserid: number; // 销户人
+        canrecommend: number; // 是否可推荐 - 0:不可 1;可 (是否推荐人)
+        createtime: string; // 创建时间
+        createtradedate: string; // 创建交易日(yyyyMMdd)
+        creatorid: number; // 创建人
+        creditquota: number; // 授信额度(金瑞)分拣室服务费(广钻)
+        hasauth: number; // 是否已实名认证 - 0:未认证 1:已认证 2:已提交(待审核) 3:已拒绝
+        hasuploaded: number; // 是否已同步(千海金) 0:未同步 1;已同步
+        isanonymous: number; // 是否匿名下单 - 0:否 1:是
+        maxinvestornum: number; // 最大用户数(经纪会员下投资者个数)
+        memberuserid: number; // 所属会员ID
+        modifierid: number; // 修改人
+        modifyremark: string; // 变更备注
+        modifysrc: number; // 修改来源 - 1:管理端 2:终端
+        modifystatus: number; // 变更状态 1 未变更 2 变更中 3 变更待审核 4 变更待复核(投资者)
+        modifytime: string; // 修改时间
+        outeruserid: string; // 外部用户ID(千海金-外部门店ID)
+        parenttopuser: string; // 上级顶级机构 [092=0,1时,默认为1, 092=2时若自已为顶级,则填入自己,自己不为顶级,填入ParentUserID的ParentTopUser]
+        parentuserid: number; // 所属机构ID
+        reckonaccountid: number; // 默认结算资金账号ID(机构分润使用) 作废
+        refercount: number; // 推荐总人数
+        refereeuserid: number; // 推荐人ID
+        refernum: string; // 推荐码
+        rootuserid: number; // 根用户ID
+        secrefercount: number; // 间接推荐总人数
+        sjaccountno: string; // 送检账户(广钻)
+        subaccountlevel: number; // 子账户层数
+        subarealevelpath: string; // 子机构层级路径(逗号分隔,首尾加逗号)
+        subjectid: number; // 所属部门(业务部门)
+        teammanageruserid: number; // 团队经理用户ID(千海金:门店代理)
+        uploadedtime: string; // 同步时间
+        userid: number; // 用户ID
+        usertype: number; // 账户类型 - 1:交易所 2:机构 3:会员子机构 4:经纪人 5:投资者 6:客户 7:企业成员(云平台)
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->获取所选客户信息对应风险率 请求 */
+    interface RiskRatioTypeForMarketerReq {
+        customertype: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->获取所选客户信息对应风险率 响应 */
+    interface RiskRatioTypeForMarketerRsp {
+        addmarginriskratio: number; // 追加保证金风险率
+        addsaferatio: number; // 追加安全度
+        createtime: string; // 创建时间
+        creatorid: number; // 创建人
+        customertype: number; // 客户类别(机构/投资者客户类别枚举项值)
+        cutbackriskratio: number; // 斩仓恢复风险率(投资者用,会员转全部净头寸)
+        cutmarginseq: string; // 斩仓市场顺序(按市场)
+        cutriskratio: number; // 斩仓风险率
+        cutsaferatio: number; // 斩仓安全度
+        cutthreshold: number; // 净值斩仓阈值(做市会员用)
+        cutthresholdflag: number; // 净值斩仓标志 - 0:不启用 1:启用(做市会员用)
+        isdefault: number; // 是否默认(每个风控模式必有且只能设置一个默认) - 0:非默认 1:默认
+        issendcancel: number; // 到达追加风险率是否撤单(交易) - 0:不撤 1:撤单
+        issenddeliverycancel: number; // 到达追加风险率是否撤单(交割) - 0:不撤 1:撤单
+        markets: string;
+        modifierid: number; // 修改人ID
+        modifytime: string; // 修改时间
+        notemarginriskratio: number; // 提示保证金风险率
+        notesaferatio: number; // 提示安全度
+        recoversaferatio: number; // 恢复正常安全度(会员)
+        recovertraderiskratio: number; // 恢复正常交易风险率(会员)
+        riskcontrolmode: number; // 适用交易用户类型 1:投资者/机构自营 2:机构做市
+        riskratiocalcmode: number; // 风险率计算方法 - 1:占用/净值
+        riskrationame: string; // 类型名称
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->新增 请求 */
+    interface AddPersonalizedReq {
+        areauserid?: number; // 经纪会员
+        autoid?: number;
+        customertype?: number; // 风险类型 code:customerType
+        groupname?: string; // 名称
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->删除 请求 */
+    interface DelInvestReq {
+        autoid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->新增 请求 */
+    interface AddInvestorForPerReq {
+        adduserids: string;
+        usergroupid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->交易商分组管理-->分组交易商获取列表-->删除 请求 */
+    interface DelInvestorForPerReq {
+        userid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->获取列表 请求 */
+    interface AccountRiskConfigReq {
+        accountid?: number; // 资金账户
+        memberuserid?: number;
+        pageNum: number; // 页码
+        pageSize: number; // 页大小
+        username?: string; // 交易商
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->获取列表 响应 */
+    interface AccountRiskConfigRsp {
+        accountid: number; // 资金账户
+        customertype: number; // 风险率类型(客户类别)
+        memberusername: string; // 所属会员
+        userid: number; // 交易商代码
+        username: string; // 交易商名称
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->详情 请求 */
+    interface InvestorViewReq {
+        accountid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->详情 响应 */
+    interface InvestorViewRsp {
+        accountid: number; // 资金账号ID
+        accountname: string;
+        customertype: number; // 投资者风险率客户类别
+        ismarketacc: number;
+        userid: number; // 用户ID
+        usertype: number;
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->新增获取交易商资金账户信息 请求 */
+    interface TaaccountListReq {
+        userid: number;
+    }
+
+    /** 交易商管理-->个性化管理-->风控个性化-->新增/修改 请求 */
+    interface AddAccountRiskConfigReq {
+        accountid: number; // 资金账户
+        customertype: number; // 风险率类型
+        userid: number; // 交易商
+    }
 
+    /** 交易商管理-->个性化管理-->风控个性化-->删除 请求 */
+    interface DeleteConfigReq {
+        accountid: number;
     }
 }