li.shaoyi hace 1 año
padre
commit
5626d04636
Se han modificado 22 ficheros con 464 adiciones y 372 borrados
  1. 57 0
      src/constants/member.ts
  2. 1 1
      src/packages/pc/views/account/tradeacct/components/details/index.vue
  3. 4 4
      src/packages/pc/views/account/tradeacct/components/sign/edit/index.vue
  4. 49 48
      src/packages/pc/views/investor/manage/user/components/details/index.vue
  5. 0 88
      src/packages/pc/views/investor/user/open/components/details/agree.vue
  6. 71 111
      src/packages/pc/views/investor/user/open/components/details/index.vue
  7. 0 67
      src/packages/pc/views/investor/user/open/components/details/refuse.vue
  8. 13 9
      src/packages/pc/views/investor/user/open/components/edit/index.vue
  9. 2 2
      src/packages/pc/views/investor/user/open/index.vue
  10. 39 0
      src/packages/pc/views/marketrun/monitor/user/components/alloffline/index.vue
  11. 50 0
      src/packages/pc/views/marketrun/monitor/user/components/offline/index.vue
  12. 77 2
      src/packages/pc/views/marketrun/monitor/user/index.vue
  13. 3 2
      src/packages/pc/views/member/institution/open/components/details/index.vue
  14. 10 7
      src/packages/pc/views/member/institution/open/components/edit/index.vue
  15. 18 13
      src/packages/pc/views/member/subinstitution/manage/components/details/index.vue
  16. 10 7
      src/packages/pc/views/member/subinstitution/manage/components/edit/index.vue
  17. 10 7
      src/packages/pc/views/member/subinstitution/user/components/edit/index.vue
  18. 1 1
      src/packages/pc/views/query/order/position/components/details/index.vue
  19. 3 3
      src/services/api/investor/index.ts
  20. 16 0
      src/services/api/market/index.ts
  21. 26 0
      src/types/model/market.d.ts
  22. 4 0
      src/types/model/member.d.ts

+ 57 - 0
src/constants/member.ts

@@ -0,0 +1,57 @@
+/**
+ * 所有者类型
+ */
+export enum UserInfoType {
+    Personal = 1, // 个人
+    Company = 2, // 企业
+}
+
+/**
+ * 获取所有者类型列表
+ * @returns 
+ */
+export function getUserInfoTypeList() {
+    return [
+        { label: '个人', value: UserInfoType.Personal },
+        { label: '企业', value: UserInfoType.Company },
+    ]
+}
+
+/**
+ * 获取所有者类型名称
+ * @param value 
+ * @returns 
+ */
+export function getUserInfoTypeName(value?: number) {
+    const item = getUserInfoTypeList().find((e) => e.value === value)
+    return item?.label ?? value
+}
+
+/**
+ * 性别
+ */
+export enum Gender {
+    Female = 0, // 女
+    Male = 1, // 男
+}
+
+/**
+ * 获取性别列表
+ * @returns 
+ */
+export function getGenderList() {
+    return [
+        { label: '男', value: Gender.Male },
+        { label: '女', value: Gender.Female },
+    ]
+}
+
+/**
+ * 获取性别名称
+ * @param value 
+ * @returns 
+ */
+export function getGenderName(value?: number) {
+    const item = getGenderList().find((e) => e.value === value)
+    return item?.label ?? value
+}

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

@@ -1,6 +1,6 @@
 <!-- 自营管理-资金账户管理-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show">
+    <app-drawer title="详情" width="900" v-model:show="show">
         <app-table-details :data="data" :label-width="160" :cell-props="detailProps" :column="2" />
         <app-table :data="data?.bankAccountsignList" v-model:columns="tableColumns" v-if="data?.isMain === 1" />
         <template #footer>

+ 4 - 4
src/packages/pc/views/account/tradeacct/components/sign/edit/index.vue

@@ -6,8 +6,7 @@
             <fieldset class="g-fieldset el-form--horizontal">
                 <legend class="g-fieldset__legend">基本信息</legend>
                 <el-form-item label="账户类型" v-if="userInfo">
-                    <span v-if="userInfo.userinfotype === 1">个人</span>
-                    <span v-else>企业</span>
+                    {{ getUserInfoTypeName(userInfo.userinfotype) }}
                 </el-form-item>
                 <el-form-item label="证件类型" v-if="userInfo">
                     {{ userInfo.cardtypeid }}
@@ -22,8 +21,8 @@
                     <app-select-bank v-model:selected="selectedBank" :cusbankid="formData.cusbankid"
                         @change="onBankChange" />
                 </el-form-item>
-                <el-form-item :label="userInfo.userinfotype === 1 ? '银行帐号' : '对公账号'" prop="bankaccountno"
-                    v-if="userInfo">
+                <el-form-item :label="userInfo.userinfotype === UserInfoType.Personal ? '银行帐号' : '对公账号'"
+                    prop="bankaccountno" v-if="userInfo">
                     <el-input v-model="formData.bankaccountno" maxlength="50" placeholder="请输入" />
                 </el-form-item>
                 <el-form-item label="银行卡户主名" prop="bankaccountname">
@@ -56,6 +55,7 @@ import { decryptAES } from '@/services/crypto'
 import { handleNoneValue } from '@/filters'
 import { useRequest } from '@/hooks/request'
 import { getSignBankById, queryCommonFlag, dealAccountSign } from '@/services/api/account'
+import { getUserInfoTypeName, UserInfoType } from '@/constants/member'
 import { queryBooleanForAmount } from '@/services/api/bank'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppSelectBank from '@pc/components/modules/select-bank/index.vue'

+ 49 - 48
src/packages/pc/views/investor/manage/user/components/details/index.vue

@@ -1,6 +1,6 @@
 <!-- 交易商管理-交易商管理-交易商管理-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show" :loading="loading" :refresh="refresh">
+    <app-drawer title="详情" width="900" v-model:show="show" :loading="loading" :refresh="refresh">
         <app-table-details title="基本信息" :data="oldData?.userAccountDetailVo" :label-width="160"
             :cell-props="detailProps1" :column="2">
             <!-- 交易商名称 -->
@@ -20,8 +20,8 @@
                 <span v-else>{{ oldData?.userAccountDetailVo.accountName }}</span>
             </template>
         </app-table-details>
-        <app-table-details title="个人资料" :data="oldData?.userinfoDetailVo" :label-width="160" :cell-props="detailProps2"
-            :column="2" v-if="oldData?.userinfoDetailVo.userinfoType === 1">
+        <app-table-details :title="oldData?.userinfoDetailVo.userinfoType === UserInfoType.Personal ? '个人资料' : '企业资料'"
+            :data="oldData?.userinfoDetailVo" :label-width="160" :cell-props="detailProps2" :column="2">
             <!-- 证件照正面 -->
             <template #cardFrontPhotoUrl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
@@ -34,13 +34,6 @@
             <template #address="{ value }">
                 {{ handleNoneValue(oldData?.userinfoDetailVo.pathName + value) }}
             </template>
-        </app-table-details>
-        <app-table-details title="企业资料" :data="oldData?.userinfoDetailVo" :label-width="160" :cell-props="detailProps3"
-            :column="2" v-else>
-            <!-- 营业执照 -->
-            <template #cardfrontphotourl="{ value }">
-                <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
-            </template>
             <!-- 法人身份证正面 -->
             <template #legalcardfrontphotourl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
@@ -61,10 +54,11 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, PropType } from 'vue'
+import { ref, PropType, computed } from 'vue'
 import { handleNoneValue, formatDate } from '@/filters'
 import { decryptAES } from '@/services/crypto'
 import { useEnum } from '@/hooks/enum'
+import { UserInfoType, getGenderName } from '@/constants/member'
 import { useRequest } from '@/hooks/request'
 import { queryInvestorListDetail } from '@/services/api/investor'
 import { CellProp } from '@pc/components/base/table-details/types'
@@ -163,43 +157,50 @@ const detailProps1: CellProp[] = [
     { prop: 'modifyRemark', label: '变更审核备注:', show: hideField },
 ]
 
-const detailProps2: CellProp[] = [
-    { prop: 'cardTypeId', label: '证件类型:', formatValue: (val) => certypepersonEnum.getEnumTypeName(val) },
-    { prop: 'cardNum', label: '证件号码:' },
-    { prop: 'company', label: '所属公司:' },
-    { prop: 'sex', label: '性别:' },
-    { prop: 'mobile', label: '手机号:' },
-    { prop: 'telPhone', label: '联系电话:' },
-    { prop: 'address', label: '通迅地址:' },
-    { prop: 'postalCode', label: '邮政编码:' },
-    { prop: 'wechat', label: '微信:' },
-    { prop: 'email', label: '邮箱:' },
-    { prop: 'cardFrontPhotoUrl', label: '证件照正面:' },
-    { prop: 'cardBackPhotoUrl', label: '证件照反面:' },
-    { prop: 'remark', label: '备注:' },
-]
-
-const detailProps3: CellProp[] = [
-    { prop: 'needInvoice', label: '是否需要发票:' },
-    { prop: 'cardTypeId', label: '证件类型:', formatValue: (val) => certypecompanyEnum.getEnumTypeName(val) },
-    { prop: 'cardNum', label: '证件号码:' },
-    { prop: 'customerName', label: '企业名称:' },
-    { prop: 'bizNature', label: '企业性质:', formatValue: (val) => biznatureEnum.getEnumTypeName(val) },
-    { prop: 'legalPersonName', label: '法人姓名:' },
-    { prop: 'contactName', label: '联系人:' },
-    { prop: 'sex', label: '性别:' },
-    { prop: 'mobile', label: '手机号:' },
-    { prop: 'telPhone', label: '联系电话:' },
-    { prop: 'address', label: '通迅地址:' },
-    { prop: 'postalCode', label: '邮政编码:' },
-    { prop: 'email', label: '邮箱:' },
-    { prop: 'wechat', label: '微信:' },
-    { prop: 'cardFrontPhotoUrl', label: '营业执照:' },
-    { prop: 'legalCardFrontPhotoUrl', label: '法人身份证正面照:' },
-    { prop: 'legalCardBackPhotoUrl', label: '法人身份证背面照:' },
-    { prop: 'otherUrl', label: '法人授权书:' },
-    { prop: 'remark', label: '备注:' },
-]
+const detailProps2 = computed<CellProp[]>(() => {
+    const type = oldData.value?.userinfoDetailVo.userinfoType
+    if (type === UserInfoType.Personal) {
+        return [
+            { prop: 'cardTypeId', label: '证件类型:', formatValue: (val) => certypepersonEnum.getEnumTypeName(val) },
+            { prop: 'cardNum', label: '证件号码:' },
+            { prop: 'company', label: '所属公司:' },
+            { prop: 'sex', label: '性别:', formatValue: (val) => getGenderName(val) },
+            { prop: 'mobile', label: '手机号:' },
+            { prop: 'telPhone', label: '联系电话:' },
+            { prop: 'address', label: '通迅地址:' },
+            { prop: 'postalCode', label: '邮政编码:' },
+            { prop: 'wechat', label: '微信:' },
+            { prop: 'email', label: '邮箱:' },
+            { prop: 'cardFrontPhotoUrl', label: '证件照正面:' },
+            { prop: 'cardBackPhotoUrl', label: '证件照反面:' },
+            { prop: 'remark', label: '备注:' },
+        ]
+    }
+    if (type === UserInfoType.Company) {
+        return [
+            { prop: 'needInvoice', label: '是否需要发票:' },
+            { prop: 'cardTypeId', label: '证件类型:', formatValue: (val) => certypecompanyEnum.getEnumTypeName(val) },
+            { prop: 'cardNum', label: '证件号码:' },
+            { prop: 'customerName', label: '企业名称:' },
+            { prop: 'bizNature', label: '企业性质:', formatValue: (val) => biznatureEnum.getEnumTypeName(val) },
+            { prop: 'legalPersonName', label: '法人姓名:' },
+            { prop: 'contactName', label: '联系人:' },
+            { prop: 'sex', label: '性别:' },
+            { prop: 'mobile', label: '手机号:' },
+            { prop: 'telPhone', label: '联系电话:' },
+            { prop: 'address', label: '通迅地址:' },
+            { prop: 'postalCode', label: '邮政编码:' },
+            { prop: 'email', label: '邮箱:' },
+            { prop: 'wechat', label: '微信:' },
+            { prop: 'cardFrontPhotoUrl', label: '营业执照:' },
+            { prop: 'legalCardFrontPhotoUrl', label: '法人身份证正面照:' },
+            { prop: 'legalCardBackPhotoUrl', label: '法人身份证背面照:' },
+            { prop: 'otherUrl', label: '法人授权书:' },
+            { prop: 'remark', label: '备注:' },
+        ]
+    }
+    return []
+})
 
 const onCancel = (isRefresh = false) => {
     show.value = false

+ 0 - 88
src/packages/pc/views/investor/user/open/components/details/agree.vue

@@ -1,88 +0,0 @@
-<!-- 会员机构管理-机构管理-机构开户申请-审核通过 -->
-<template>
-    <app-drawer title="审核通过" width="480" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" label-width="130px" :model="formData" :rules="formRules" :show-message="false">
-            <el-form-item label="机构代码" prop="areacode">
-                <el-input type="number" v-model="formData.areacode" placeholder="四位数字" />
-            </el-form-item>
-            <el-form-item label="管理员登录账号" prop="smlogincode">
-                <el-input v-model="formData.smlogincode" maxlength="50" placeholder="输入必须包含字母且最少3位" />
-            </el-form-item>
-            <el-form-item label="管理员名称" prop="loginname">
-                <el-input v-model="formData.loginname" maxlength="50" placeholder="请输入" />
-            </el-form-item>
-            <el-form-item label="默认登录密码" prop="smpassword">
-                <el-input v-model="formData.smpassword" maxlength="50" placeholder="请输入" disabled />
-            </el-form-item>
-        </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, toRaw } from 'vue'
-import { ElMessage, FormInstance, FormRules } from 'element-plus'
-import { wskhUserinfoApproved } from '@/services/api/member'
-import AppDrawer from '@pc/components/base/drawer/index.vue'
-
-const props = defineProps({
-    selectedRow: {
-        type: Object as PropType<Model.MemberDetail>,
-        required: true
-    }
-})
-
-const formRef = ref<FormInstance>()
-const show = ref(true)
-const refresh = ref(false)
-const loading = ref(false)
-
-const formData = ref<Partial<Model.MemberDetail>>({
-    userid: props.selectedRow.userid,
-    auditflag: 1,
-    smpassword: '123456'
-})
-
-// 表单验证规则
-const formRules: FormRules = {
-    areacode: [{ required: true, min: 4, max: 4 }],
-    smlogincode: [{
-        required: true,
-        validator: (rule, value, callback) => {
-            if (value?.length > 2 && /[a-zA-Z]/.test(value)) {
-                callback()
-            } else {
-                callback(new Error('输入必须包含字母且最少3位'))
-            }
-        }
-    }],
-    loginname: [{ required: true }]
-}
-
-const onCancel = (isRefresh = false) => {
-    show.value = false
-    refresh.value = isRefresh
-}
-
-const onSubmit = () => {
-    formRef.value?.validate((valid) => {
-        if (valid) {
-            loading.value = true
-            wskhUserinfoApproved({
-                data: toRaw(formData.value)
-            }).then(() => {
-                ElMessage.success('提交成功')
-                onCancel(true)
-            }).catch((err) => {
-                ElMessage.error('提交失败:' + err)
-                //onCancel()
-            }).finally(() => {
-                loading.value = false
-            })
-        }
-    })
-}
-</script>

+ 71 - 111
src/packages/pc/views/investor/user/open/components/details/index.vue

@@ -1,18 +1,7 @@
-<!-- 会员机构管理-机构管理-机构开户申请-详情 -->
+<!-- 交易商管理-开户管理-交易商开户-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show" :loading="loading" :refresh="refresh">
-        <app-table-details title="基本信息" :data="data" :label-width="160" :cell-props="detailProps1" :column="2">
-            <!-- 创建时间 -->
-            <template #createtime="{ value }">
-                {{ formatDate(value) }}
-            </template>
-        </app-table-details>
-        <app-table-details title="个人资料" :data="data" :label-width="160" :cell-props="detailProps2" :column="2"
-            v-if="isPerson">
-            <!-- 证件类型 -->
-            <template #cardtype="{ value }">
-                {{ certypepersonEnum.getEnumTypeName(value) }}
-            </template>
+    <app-drawer title="详情" width="900" v-model:show="show">
+        <app-table-details :data="data" :label-width="140" :cell-props="detailProps" :column="2">
             <!-- 证件照正面 -->
             <template #cardfrontphotourl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
@@ -21,18 +10,8 @@
             <template #cardbackphotourl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
             </template>
-        </app-table-details>
-        <app-table-details title="企业资料" :data="data" :label-width="160" :cell-props="detailProps3" :column="2" v-else>
-            <!-- 企业性质 -->
-            <template #biznature="{ value }">
-                {{ biznatureEnum.getEnumTypeName(value) }}
-            </template>
-            <!-- 证件类型 -->
-            <template #cardtype="{ value }">
-                {{ certypecompanyEnum.getEnumTypeName(value) }}
-            </template>
-            <!-- 证件照正面 -->
-            <template #cardfrontphotourl="{ value }">
+            <!-- 手持证件照 -->
+            <template #halfbodyphotourl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
             </template>
             <!-- 法人身份证正面 -->
@@ -43,74 +22,64 @@
             <template #legalcardbackphotourl="{ value }">
                 <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
             </template>
+            <!-- 法人授权书 -->
+            <template #otherurl="{ value }">
+                <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
+            </template>
+            <!-- 银行卡正面 -->
+            <template #bankcardfrontphotourl="{ value }">
+                <el-image :src="value" fit="cover" lazy style="width: 128px; height: 72px" />
+            </template>
         </app-table-details>
-        <app-table-details title="附件" :data="data" :label-width="160" :cell-props="detailProps4" :column="2" />
         <template #footer>
             <el-button @click="onCancel(false)">关闭</el-button>
-            <template v-if="data?.userstate === 2">
-                <el-button @click="openComponent('Refuse')" type="primary">审核拒绝</el-button>
-                <el-button @click="openComponent('Agree')" type="primary">审核通过</el-button>
-            </template>
         </template>
-        <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
-            @closed="closeComponent" v-if="componentId" />
     </app-drawer>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, PropType, defineAsyncComponent } from 'vue'
+import { shallowRef, PropType, computed } from 'vue'
 import { ElMessage } from 'element-plus'
-import { formatDate } from '@/filters'
+import { decryptAES } from '@/services/crypto'
+import { getUserInfoTypeName, getGenderName, UserInfoType } from '@/constants/member'
 import { useEnum } from '@/hooks/enum'
-import { useComponent } from '@/hooks/component'
 import { useRequest } from '@/hooks/request'
-import { queryWskhUserinfoDetail } from '@/services/api/member'
+import { queryInvestorDetail } 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'
 import service from '@/services'
 
 const props = defineProps({
     selectedRow: {
-        type: Object as PropType<Model.MemberDetail>,
+        type: Object as PropType<Model.InvestorRsp>,
         required: true
     }
 })
 
-const componentMap = new Map<string, unknown>([
-    ['Agree', defineAsyncComponent(() => import('./agree.vue'))], // 同意
-    ['Refuse', defineAsyncComponent(() => import('./refuse.vue'))], // 拒绝
-])
-
 const show = shallowRef(true)
 const refresh = shallowRef(false)
-const loading = shallowRef(false)
-// 是否个人
-const isPerson = props.selectedRow.userinfotype === 1
-// 企业性质枚举
-const biznatureEnum = useEnum('biznature')
-// 个人证件类型
-const certypepersonEnum = useEnum('certypeperson')
-// 企业证件类型
-const certypecompanyEnum = useEnum('certypecompany')
 
-const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => onCancel(true))
+// 证件类型
+const certificatetypeEnum = useEnum('certificatetype')
+// 开户状态
+const userstateEnum = useEnum('investorOpenStatus')
 
-const { data } = useRequest(queryWskhUserinfoDetail, {
+const { data } = useRequest(queryInvestorDetail, {
     params: {
-        userId: props.selectedRow.userid
+        userId: props.selectedRow.userId,
     },
-    onSuccess: (res) => {
-        const { cardfrontphotourl, cardbackphotourl, legalcardfrontphotourl, legalcardbackphotourl, attachment1, attachment2 } = res.data
+    onSuccess: () => {
         if (data.value) {
             const baseUrl = service.getConfig('apiUrl')
             const getUrl = (value: string) => value && new URL(value, baseUrl).href
 
-            data.value.cardfrontphotourl = getUrl(cardfrontphotourl)
-            data.value.cardbackphotourl = getUrl(cardbackphotourl)
-            data.value.legalcardfrontphotourl = getUrl(legalcardfrontphotourl)
-            data.value.legalcardbackphotourl = getUrl(legalcardbackphotourl)
-            data.value.attachment1 = getUrl(attachment1)
-            data.value.attachment2 = getUrl(attachment2)
+            data.value.cardfrontphotourl = getUrl(data.value.cardfrontphotourl)
+            data.value.cardbackphotourl = getUrl(data.value.cardbackphotourl)
+            data.value.halfbodyphotourl = getUrl(data.value.halfbodyphotourl)
+            data.value.legalcardfrontphotourl = getUrl(data.value.legalcardfrontphotourl)
+            data.value.legalcardbackphotourl = getUrl(data.value.legalcardbackphotourl)
+            data.value.otherurl = getUrl(data.value.otherurl)
         }
     },
     onError: (err) => {
@@ -118,54 +87,45 @@ const { data } = useRequest(queryWskhUserinfoDetail, {
     }
 })
 
-const detailProps1 = [
-    { prop: 'username', label: '机构名称:' },
-    { prop: 'userinfotype', label: '所有者类型:' },
-    { prop: 'usertype', label: '机构类型:' },
-    { prop: 'referral', label: '推荐人:' },
-    { prop: 'userstate', label: '开户状态:' },
-    { prop: 'createtime', label: '创建时间:' },
-]
-
-const detailProps2 = [
-    { prop: 'customername', label: '名称:' },
-    { prop: 'company', label: '公司:' },
-    { prop: 'cardtype', label: '证件类型:' },
-    { prop: 'cardnum', label: '证件号码:' },
-    { prop: 'cardfrontphotourl', label: '证件照正面:' },
-    { prop: 'cardbackphotourl', label: '证件照反面:' },
-    { prop: 'sex', label: '性别:' },
-    { prop: 'mobilephone', label: '手机号:' },
-    { prop: 'telphone', label: '联系电话:' },
-    { prop: 'address', label: '地址:' },
-    { prop: 'postalcode', label: '邮政编码:' },
-    { prop: 'email', label: '邮箱:' },
-    { prop: 'remark', label: '备注:' },
-]
-
-const detailProps3 = [
-    { prop: 'customername', label: '名称:' },
-    { prop: 'biznature', label: '企业性质:' },
-    { prop: 'cardtype', label: '证件类型:' },
-    { prop: 'cardnum', label: '证件号码:' },
-    { prop: 'cardfrontphotourl', label: '证件照正面:' },
-    { prop: 'legalpersonname', label: '法人姓名:' },
-    { prop: 'legalcardfrontphotourl', label: '法人身份证正面:' },
-    { prop: 'legalcardbackphotourl', label: '法人身份证反面:' },
-    { prop: 'contactname', label: '联系人:' },
-    { prop: 'sex', label: '性别:' },
-    { prop: 'mobilephone', label: '手机号:' },
-    { prop: 'telphone', label: '联系电话:' },
-    { prop: 'address', label: '地址:' },
-    { prop: 'postalcode', label: '邮政编码:' },
-    { prop: 'email', label: '邮箱:' },
-    { prop: 'remark', label: '备注:' },
-]
-
-const detailProps4 = [
-    { prop: 'attachment1', label: '附件1:' },
-    { prop: 'attachment2', label: '附件2:' },
-]
+const detailProps = computed(() => {
+    const isPerson = data.value?.userinfotype === UserInfoType.Personal
+    const result: CellProp[] = [
+        { prop: 'username', formatLabel: () => isPerson ? '用户名称:' : '企业名称:' },
+        { prop: 'userinfotype', label: '信息类型:', formatValue: (val) => getUserInfoTypeName(val) },
+        { prop: 'cardtype', label: '证件类型:', formatValue: (val) => certificatetypeEnum.getEnumTypeName(val) },
+        { prop: 'cardnum', label: '证件号码:', formatValue: (val) => decryptAES(val) },
+        { prop: 'pathname', label: '地址:' },
+        { prop: 'legalpersonname', label: '法人名称:', show: !isPerson },
+        { prop: 'contactname', label: '联系人:', show: !isPerson },
+        { prop: 'sex', label: '性别:', formatValue: (val) => getGenderName(val) },
+        { prop: 'mobilephone', label: '手机号:', formatValue: (val) => decryptAES(val) },
+        { prop: 'postalcode', label: '邮政编码:' },
+        { prop: 'wechat', label: '微信:', formatValue: (val) => decryptAES(val) },
+        { prop: 'telphone', label: '联系电话:', formatValue: (val) => decryptAES(val) },
+        { prop: 'email', label: '邮箱:', formatValue: (val) => decryptAES(val) },
+        { prop: 'company', label: '所属公司:', show: isPerson },
+        { prop: 'memberusername', label: '所属会员:' },
+        { prop: 'areaname', label: '所属机构:' },
+        { prop: 'brokername', label: '经纪人:' },
+        { prop: 'referralname', label: '推荐人:' },
+        { prop: 'cardfrontphotourl', formatLabel: () => isPerson ? '证件照正面:' : '营业执照:' },
+        { prop: 'cardbackphotourl', label: '证件照反面:', show: !!data.value?.cardbackphotourl },
+        { prop: 'halfbodyphotourl', label: '手持证件照:', show: !!data.value?.halfbodyphotourl },
+        { prop: 'legalcardfrontphotourl', label: '身份证正面照:', show: !isPerson },
+        { prop: 'legalcardbackphotourl', label: '身份证背面照:', show: !isPerson },
+        { prop: 'otherurl', label: '法人授权书:', show: !isPerson },
+        { prop: 'userstate', label: '状态:', formatValue: (val) => userstateEnum.getEnumTypeName(val) },
+        { prop: 'remark', label: '备注:' },
+    ]
+    if (data.value?.bankid) {
+        result.push(
+            { prop: 'bankname', label: '银行名称:' },
+            { prop: 'bankaccount', label: '银行账户:' },
+            { prop: 'bankcardfrontphotourl', label: '银行卡正面:' },
+        )
+    }
+    return result
+})
 
 const onCancel = (isRefresh = false) => {
     show.value = false

+ 0 - 67
src/packages/pc/views/investor/user/open/components/details/refuse.vue

@@ -1,67 +0,0 @@
-<!-- 会员机构管理-机构管理-机构开户申请-审核拒绝 -->
-<template>
-    <app-drawer title="审核拒绝" width="480" v-model:show="show" :loading="loading" :refresh="refresh">
-        <el-form ref="formRef" label-width="100px" :model="formData" :rules="formRules" :show-message="false">
-            <el-form-item label="不通过原因" prop="remark">
-                <el-input type="textarea" v-model="formData.remark" maxlength="200" :rows="5" placeholder="请输入" />
-            </el-form-item>
-        </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, toRaw } from 'vue'
-import { ElMessage, FormInstance, FormRules } from 'element-plus'
-import { wskhUserinfoApproved } from '@/services/api/member'
-import AppDrawer from '@pc/components/base/drawer/index.vue'
-
-const props = defineProps({
-    selectedRow: {
-        type: Object as PropType<Model.MemberDetail>,
-        required: true
-    }
-})
-
-const formRef = ref<FormInstance>()
-const show = ref(true)
-const refresh = ref(false)
-const loading = ref(false)
-
-const formData = ref<Partial<Model.MemberDetail>>({
-    userid: props.selectedRow.userid,
-    auditflag: 2
-})
-
-// 表单验证规则
-const formRules: FormRules = {
-    remark: [{ required: true }],
-}
-
-const onCancel = (isRefresh = false) => {
-    show.value = false
-    refresh.value = isRefresh
-}
-
-const onSubmit = () => {
-    formRef.value?.validate((valid) => {
-        if (valid) {
-            loading.value = true
-            wskhUserinfoApproved({
-                data: toRaw(formData.value)
-            }).then(() => {
-                ElMessage.success('提交成功')
-                onCancel(true)
-            }).catch((err) => {
-                ElMessage.error('提交失败:' + err)
-                onCancel()
-            }).finally(() => {
-                loading.value = false
-            })
-        }
-    })
-}
-</script>

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

@@ -17,8 +17,9 @@
             </el-form-item>
             <el-form-item class="el-form-item--row" label="信息类型" prop="userinfotype">
                 <el-radio-group v-model="formData.userinfotype">
-                    <el-radio label="个人" :value="1" />
-                    <el-radio label="企业" :value="2" />
+                    <template v-for="item in getUserInfoTypeList()" :key="item.value">
+                        <el-radio :label="item.label" :value="item.value" />
+                    </template>
                 </el-radio-group>
             </el-form-item>
             <template v-if="isPerson">
@@ -27,8 +28,9 @@
                 </el-form-item>
                 <el-form-item label="性别" prop="sex">
                     <el-radio-group v-model="formData.sex">
-                        <el-radio label="男" :value="1" />
-                        <el-radio label="女" :value="0" />
+                        <template v-for="item in getGenderList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
             </template>
@@ -91,8 +93,9 @@
                 </el-form-item>
                 <el-form-item label="性别" prop="sex">
                     <el-radio-group v-model="formData.sex">
-                        <el-radio label="男" :value="1" />
-                        <el-radio label="女" :value="0" />
+                        <template v-for="item in getGenderList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
             </template>
@@ -130,6 +133,7 @@ import { ref, reactive, PropType, onMounted, computed } from 'vue'
 import { ElMessage, FormInstance, FormRules, UploadUserFile } from 'element-plus'
 import { useRequest } from '@/hooks/request'
 import { dealSunOrgan, queryInvestorDetail } from '@/services/api/investor'
+import { getUserInfoTypeList, getGenderList, UserInfoType, Gender } from '@/constants/member'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppUpload from '@pc/components/base/upload/index.vue'
 import AppEnum from '@pc/components/modules/enum/index.vue'
@@ -165,11 +169,11 @@ const uploadFiles = reactive<{
 
 
 // 是否个人
-const isPerson = computed(() => formData.value.userinfotype === 1)
+const isPerson = computed(() => formData.value.userinfotype === UserInfoType.Personal)
 
 const formData = ref<Partial<Model.MemberDetail>>({
-    userinfotype: 1,
-    sex: 1
+    userinfotype: UserInfoType.Personal,
+    sex: Gender.Male
 })
 
 const getUploadFiles = (value?: string) => {

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

@@ -96,9 +96,9 @@ const handleOperateButtons = (row: Model.InvestorRsp) => {
     switch (row.userState) {
         case 1:
         case 3:
-            return getFilteredButtons(['investor_user_open_modify', 'investor_user_open_delete'])
+            return getFilteredButtons(['investor_user_open_details', 'investor_user_open_modify', 'investor_user_open_delete'])
         case 2:
-            return getFilteredButtons(['investor_user_open_cancel'])
+            return getFilteredButtons(['investor_user_open_details', 'investor_user_open_cancel'])
         default:
             return []
     }

+ 39 - 0
src/packages/pc/views/marketrun/monitor/user/components/alloffline/index.vue

@@ -0,0 +1,39 @@
+<!-- 市场运行管理-监控管理-在线用户查询-强制所有用户下线 -->
+<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 } from 'vue'
+import { ElMessage } from 'element-plus'
+import { marketRunDownonline } from '@/services/api/market'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+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
+    marketRunDownonline().then(() => {
+        ElMessage.success('操作成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('操作失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 50 - 0
src/packages/pc/views/marketrun/monitor/user/components/offline/index.vue

@@ -0,0 +1,50 @@
+<!-- 市场运行管理-监控管理-在线用户查询-强制下线 -->
+<template>
+    <app-drawer title="提示" v-model:show="show" :loading="loading" :refresh="refresh">
+        <div class="g-text-message">是否将用户[{{ selectedRow.loginid }}]强制下线?</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 { marketRunDownonline } from '@/services/api/market'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.MarketRunUserLoginRsp>,
+        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
+    marketRunDownonline({
+        data: {
+            loginid: props.selectedRow.loginid.toString()
+        }
+    }).then(() => {
+        ElMessage.success('操作成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('操作失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 77 - 2
src/packages/pc/views/marketrun/monitor/user/index.vue

@@ -1,7 +1,82 @@
-<!-- 市场运行管理-监控管理-持仓汇总监控 -->
+<!-- 市场运行管理-监控管理-在线用户查询 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <template #header>
+            <app-filter :options="filterOptons" />
+        </template>
+        <app-table :data="dataList" v-model:columns="tableColumns" :loading="loading">
+            <template #headerLeft>
+                <app-operation :data-list="getFilteredButtons(['marketrun_monitor_user_alloffline'])"
+                    @click="openComponent" />
+            </template>
+            <!-- 操作 -->
+            <template #operate="{ row }">
+                <app-operation size="small" :data-list="getFilteredButtons(['marketrun_monitor_user_offline'])"
+                    @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="{ selectedRow }" @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 { useRequest } from '@/hooks/request'
+import { useDataFilter } from '@/hooks/datatable'
+import { useOperation } from '@/hooks/operation'
+import { marketRunUserLogin } from '@/services/api/market'
+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 { filterOptons, getQueryParams } = useDataFilter<Model.MarketRunUserLoginReq>()
+
+const { componentMap, componentId, selectedRow, openComponent, closeComponent, getFilteredButtons } = useOperation<Model.MarketRunUserLoginRsp>({
+    onClose: () => onSearch()
+})
+
+const { dataList, total, pageSize, pageIndex, loading, run } = useRequest(marketRunUserLogin, {
+    params: {
+        pageNum: 1,
+        pageSize: 20,
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const tableColumns = shallowRef<Model.TableColumn[]>([
+    { field: 'loginid', label: '登录账号' },
+    { field: 'username', label: '账户名称' },
+    { field: 'loginip', label: 'IP' },
+    { field: 'loginport', label: '端口' },
+    { field: 'logintime', label: '登录时间', formatValue: (val) => formatDate(val) },
+    { field: 'clienttype', label: '终端类型' },
+    { field: 'softversion', label: '软件版本' },
+    { field: 'areaname', label: '所属机构' },
+    { field: 'membername', label: '所属会员' },
+    { field: 'operate', label: '操作', fixed: 'right' }
+])
+
+filterOptons.inputList = [
+    { label: '登录账号', keys: ['loginid'], type: 'number' },
+]
+
+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>

+ 3 - 2
src/packages/pc/views/member/institution/open/components/details/index.vue

@@ -1,6 +1,6 @@
 <!-- 会员机构管理-机构管理-机构开户申请-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show" :loading="loading" :refresh="refresh">
+    <app-drawer title="详情" width="900" v-model:show="show" :loading="loading" :refresh="refresh">
         <app-table-details title="基本信息" :data="data" :label-width="160" :cell-props="detailProps1" :column="2">
             <!-- 创建时间 -->
             <template #createtime="{ value }">
@@ -64,6 +64,7 @@ import { formatDate } from '@/filters'
 import { useEnum } from '@/hooks/enum'
 import { useComponent } from '@/hooks/component'
 import { useRequest } from '@/hooks/request'
+import { UserInfoType } from '@/constants/member'
 import { queryWskhUserinfoDetail } from '@/services/api/member'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppTableDetails from '@pc/components/base/table-details/index.vue'
@@ -85,7 +86,7 @@ const show = shallowRef(true)
 const refresh = shallowRef(false)
 const loading = shallowRef(false)
 // 是否个人
-const isPerson = props.selectedRow.userinfotype === 1
+const isPerson = props.selectedRow.userinfotype === UserInfoType.Personal
 // 企业性质枚举
 const biznatureEnum = useEnum('biznature')
 // 个人证件类型

+ 10 - 7
src/packages/pc/views/member/institution/open/components/edit/index.vue

@@ -9,8 +9,9 @@
                 </el-form-item>
                 <el-form-item label="所有者类型" prop="userinfotype">
                     <el-radio-group v-model="formData.userinfotype">
-                        <el-radio label="企业" :value="2" />
-                        <el-radio label="个人" :value="1" />
+                        <template v-for="item in getUserInfoTypeList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="推荐人" prop="referral">
@@ -61,8 +62,9 @@
                 </template>
                 <el-form-item label="性别" prop="sex">
                     <el-radio-group v-model="formData.sex">
-                        <el-radio label="男" :value="1" />
-                        <el-radio label="女" :value="0" />
+                        <template v-for="item in getGenderList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="手机号" prop="mobilephone">
@@ -113,6 +115,7 @@ import { ElMessage, FormInstance, FormRules, UploadUserFile } from 'element-plus
 import { decryptAES } from '@/services/crypto'
 import { useRequest } from '@/hooks/request'
 import { wskhUserinfoAdd, queryWskhUserinfoDetail } from '@/services/api/member'
+import { getUserInfoTypeList, getGenderList, UserInfoType, Gender } from '@/constants/member'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppUpload from '@pc/components/base/upload/index.vue'
 import AppEnum from '@pc/components/modules/enum/index.vue'
@@ -148,11 +151,11 @@ const uploadFiles = reactive<{
 
 
 // 是否个人
-const isPerson = computed(() => formData.value.userinfotype === 1)
+const isPerson = computed(() => formData.value.userinfotype === UserInfoType.Personal)
 
 const formData = ref<Partial<Model.MemberDetail>>({
-    userinfotype: 2,
-    sex: 1
+    userinfotype: UserInfoType.Company,
+    sex: Gender.Male
 })
 
 const getUploadFiles = (value?: string) => {

+ 18 - 13
src/packages/pc/views/member/subinstitution/manage/components/details/index.vue

@@ -1,6 +1,6 @@
 <!-- 会员机构管理-子机构管理-子机构管理-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show" :loading="loading" :refresh="refresh">
+    <app-drawer title="详情" width="900" v-model:show="show" :loading="loading" :refresh="refresh">
         <app-table-details title="基本信息" :data="oldData?.userAccountDetailVo" :label-width="160"
             :cell-props="detailProps1" :column="2">
             <!-- 机构名称 -->
@@ -11,7 +11,7 @@
                 </p>
             </template>
         </app-table-details>
-        <app-table-details :title="oldData?.userinfoDetailVo.userinfoType === 1 ? '个人资料' : '企业资料'"
+        <app-table-details :title="oldData?.userinfoDetailVo.userinfoType === UserInfoType.Personal ? '个人资料' : '企业资料'"
             :data="oldData?.userinfoDetailVo" :label-width="160" :cell-props="detailProps2" :column="2"
             v-if="detailProps2.length">
             <!-- 名称 -->
@@ -30,10 +30,13 @@
             </template>
             <!-- 企业性质 -->
             <template #bizNature="{ value }">
-                <p>{{ biznatureEnum.getEnumTypeName(value) }}</p>
-                <p class="red" v-if="newData && newData.userinfoDetailVo.bizNature !== value">
-                    {{ biznatureEnum.getEnumTypeName(newData.userinfoDetailVo.bizNature) }}
-                </p>
+                <template v-if="value">
+                    <p>{{ biznatureEnum.getEnumTypeName(value) }}</p>
+                    <p class="red" v-if="newData && newData.userinfoDetailVo.bizNature !== value">
+                        {{ biznatureEnum.getEnumTypeName(newData.userinfoDetailVo.bizNature) }}
+                    </p>
+                </template>
+                <template v-else>{{ handleNoneValue() }}</template>
             </template>
             <!-- 证件类型 -->
             <template #cardTypeId="{ value }">
@@ -186,6 +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 { useRequest } from '@/hooks/request'
 import { organSonViewson } from '@/services/api/member'
 import { CellProp } from '@pc/components/base/table-details/types'
@@ -225,7 +229,7 @@ const { data } = useRequest(organSonViewson, {
         const baseUrl = service.getConfig('apiUrl')
         const getUrl = (value: string) => value && new URL(value, baseUrl).href
 
-        if (newResult) {
+        if (newResult?.userinfoDetailVo) {
             const detail = newResult.userinfoDetailVo
             detail.cardFrontPhotoUrl = getUrl(detail.cardFrontPhotoUrl)
             detail.cardBackPhotoUrl = getUrl(detail.cardBackPhotoUrl)
@@ -239,8 +243,10 @@ const { data } = useRequest(organSonViewson, {
             detail.telPhone = decryptAES(detail.telPhone)
             detail.wechat = decryptAES(detail.wechat)
             detail.email = decryptAES(detail.email)
+
+            newData.value = newResult
         }
-        if (oldResult) {
+        if (oldResult?.userinfoDetailVo) {
             const detail = oldResult.userinfoDetailVo
             detail.cardFrontPhotoUrl = getUrl(detail.cardFrontPhotoUrl)
             detail.cardBackPhotoUrl = getUrl(detail.cardBackPhotoUrl)
@@ -254,10 +260,9 @@ const { data } = useRequest(organSonViewson, {
             detail.telPhone = decryptAES(detail.telPhone)
             detail.wechat = decryptAES(detail.wechat)
             detail.email = decryptAES(detail.email)
-        }
 
-        newData.value = newResult
-        oldData.value = oldResult
+            oldData.value = oldResult
+        }
     }
 })
 
@@ -272,7 +277,7 @@ const detailProps1: CellProp[] = [
 
 const detailProps2 = computed<CellProp[]>(() => {
     const type = oldData.value?.userinfoDetailVo.userinfoType
-    if (type === 1) {
+    if (type === UserInfoType.Personal) {
         return [
             { prop: 'customerName', label: '名称:' },
             { prop: 'company', label: '公司:' },
@@ -291,7 +296,7 @@ const detailProps2 = computed<CellProp[]>(() => {
             { prop: 'remark', label: '备注:' },
         ]
     }
-    if (type === 2) {
+    if (type === UserInfoType.Company) {
         return [
             { prop: 'customerName', label: '名称:' },
             { prop: 'bizNature', label: '企业性质:' },

+ 10 - 7
src/packages/pc/views/member/subinstitution/manage/components/edit/index.vue

@@ -9,8 +9,9 @@
                 </el-form-item>
                 <el-form-item label="所有者类型" prop="userinfotype">
                     <el-radio-group v-model="formData.userinfotype" :disabled="!!selectedRow">
-                        <el-radio label="企业" :value="2" />
-                        <el-radio label="个人" :value="1" />
+                        <template v-for="item in getUserInfoTypeList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="机构代码" prop="userid">
@@ -67,8 +68,9 @@
                 </template>
                 <el-form-item label="性别" prop="sex">
                     <el-radio-group v-model="formData.sex">
-                        <el-radio label="男" :value="1" />
-                        <el-radio label="女" :value="0" />
+                        <template v-for="item in getGenderList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="手机号" prop="mobile">
@@ -132,6 +134,7 @@ import { ElMessage, FormInstance, FormRules, UploadUserFile } from 'element-plus
 import { decryptAES } from '@/services/crypto'
 import { useRequest } from '@/hooks/request'
 import { organSonViewson, organSonAdd, organSonSave, organSonEdit } from '@/services/api/member'
+import { getUserInfoTypeList, getGenderList, UserInfoType, Gender } from '@/constants/member'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppUpload from '@pc/components/base/upload/index.vue'
 import AppEnum from '@pc/components/modules/enum/index.vue'
@@ -170,12 +173,12 @@ const uploadFiles = reactive<{
 
 
 // 是否个人
-const isPerson = computed(() => formData.value.userinfotype === 1)
+const isPerson = computed(() => formData.value.userinfotype === UserInfoType.Personal)
 
 const formData = ref<Partial<Model.organSonUpdateReq>>({
     parentuserid: props.selectedParent?.userid,
-    userinfotype: 2,
-    sex: 1,
+    userinfotype: UserInfoType.Company,
+    sex: Gender.Male,
     usertype: 3
 })
 

+ 10 - 7
src/packages/pc/views/member/subinstitution/user/components/edit/index.vue

@@ -12,8 +12,9 @@
                 </el-form-item>
                 <el-form-item label="所有者类型" prop="userinfotype">
                     <el-radio-group v-model="formData.userinfotype">
-                        <el-radio label="企业" :value="2" />
-                        <el-radio label="个人" :value="1" />
+                        <template v-for="item in getUserInfoTypeList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="机构名称" prop="username">
@@ -64,8 +65,9 @@
                 </template>
                 <el-form-item label="性别" prop="sex">
                     <el-radio-group v-model="formData.sex">
-                        <el-radio label="男" :value="1" />
-                        <el-radio label="女" :value="0" />
+                        <template v-for="item in getGenderList()" :key="item.value">
+                            <el-radio :label="item.label" :value="item.value" />
+                        </template>
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="手机号" prop="mobilephone">
@@ -127,6 +129,7 @@ import { ref, reactive, PropType, onMounted, computed } from 'vue'
 import { ElMessage, FormInstance, FormRules, UploadUserFile } from 'element-plus'
 import { useRequest } from '@/hooks/request'
 import { wskhUserinfoAdd, queryWskhUserinfoDetail } from '@/services/api/member'
+import { getUserInfoTypeList, getGenderList, UserInfoType, Gender } from '@/constants/member'
 import AppDrawer from '@pc/components/base/drawer/index.vue'
 import AppUpload from '@pc/components/base/upload/index.vue'
 import AppEnum from '@pc/components/modules/enum/index.vue'
@@ -162,11 +165,11 @@ const uploadFiles = reactive<{
 
 
 // 是否个人
-const isPerson = computed(() => formData.value.userinfotype === 1)
+const isPerson = computed(() => formData.value.userinfotype === UserInfoType.Personal)
 
 const formData = ref<Partial<Model.MemberDetail>>({
-    userinfotype: 2,
-    sex: 1
+    userinfotype: UserInfoType.Company,
+    sex: Gender.Male
 })
 
 const getUploadFiles = (value?: string) => {

+ 1 - 1
src/packages/pc/views/query/order/position/components/details/index.vue

@@ -1,6 +1,6 @@
 <!-- 查询管理-内部订单查询-持仓单查询-详情 -->
 <template>
-    <app-drawer title="详情" width="860" v-model:show="show">
+    <app-drawer title="详情" width="900" v-model:show="show">
         <app-table-details :data="data" :label-width="120" :cell-props="detailProps" :column="2" />
         <template #footer>
             <el-button @click="onCancel(false)">关闭</el-button>

+ 3 - 3
src/services/api/investor/index.ts

@@ -2,21 +2,21 @@ import httpClient from '@/services/http'
 import { CommonFetchOptions } from '@/services/http/types'
 
 /**
- * 获取子机构开户列表信息
+ * 交易商开户列表信息获取
  */
 export function queryInvestor(options: CommonFetchOptions<{ request: Model.InvestorReq; response: Model.InvestorRsp; }>) {
     return httpClient.commonRequest('/investor/query', 'get', options)
 }
 
 /**
- * 获取子机构开户列表详情信息
+ * 获取交易商开户详情信息
  */
 export function queryInvestorDetail(options: CommonFetchOptions<{ request: Model.InvestorDetailReq; response: Model.MemberDetail; }>) {
     return httpClient.commonRequest('/investor/querySunDetail', 'get', options)
 }
 
 /**
- * 子机构开户
+ * 交易商开户
  */
 export function dealSunOrgan(options: CommonFetchOptions<{ request: Partial<Model.MemberDetail>; }>) {
     return httpClient.commonRequest('/investor/dealSunOrgan', 'post', options)

+ 16 - 0
src/services/api/market/index.ts

@@ -0,0 +1,16 @@
+import httpClient from '@/services/http'
+import { CommonFetchOptions } from '@/services/http/types'
+
+/**
+ * 市场运行管理-->监控管理-->在线用户查询-->获取列表
+ */
+export function marketRunUserLogin(options: CommonFetchOptions<{ request: Model.MarketRunUserLoginReq; response: Model.MarketRunUserLoginRsp; }>) {
+    return httpClient.commonRequest('/marketRun/queryUserLogin', 'get', options)
+}
+
+/**
+ * 市场运行管理-->监控管理-->在线用户查询-->强制下线
+ */
+export function marketRunDownonline(options: CommonFetchOptions<{ request: Model.marketRunDownonlineReq; }> = {}) {
+    return httpClient.commonRequest('/marketRun/downonline', 'get', options)
+}

+ 26 - 0
src/types/model/market.d.ts

@@ -0,0 +1,26 @@
+declare namespace Model {
+    /** 市场运行管理-->监控管理-->在线用户查询-->获取列表 请求 */
+    interface MarketRunUserLoginReq {
+        loginid?: string;
+        pageNum: number;
+        pageSize: number;
+    }
+
+    /** 市场运行管理-->监控管理-->在线用户查询-->获取列表 响应 */
+    interface MarketRunUserLoginRsp {
+        areaname: string; // 所属机构
+        clienttype: number; // 终端类型
+        loginid: number; // 登录账号
+        loginip: string; // IP
+        loginport: number; // 端口
+        logintime: string; // 登录时间
+        membername: string; // 所属会员
+        softversion: string; // 软件版本
+        username: string; // 账户名称
+    }
+
+    /** 市场运行管理-->监控管理-->在线用户查询-->强制下线 请求 */
+    interface marketRunDownonlineReq {
+        loginid?: string;
+    }
+}

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

@@ -6,6 +6,7 @@ declare namespace Model {
         allocated: number;
         areacode: string; // 机构代码
         areaid: number; // 机构Id
+        areaname: string; // 所属机构
         attachment1: string; // 附件1
         attachment2: string; // 附件2
         attachment3: string; // 附件3
@@ -27,6 +28,7 @@ declare namespace Model {
         bizscope: string; // 企业经营范围(企业)
         biztype: number; // 企业类型 - 1:进口/生产 2:销售 3:零售 4:运输 5:仓储
         brokerid: string; // 经纪人ID(所属客户经理-千海金)
+        brokername: string; // 经纪人
         cardaddress: string; // 证件地址(用于存储通讯详情地址)
         cardbackphotourl: string; // 背面证件照地址
         cardfrontphotourl: string; // 正面证件照地址
@@ -57,6 +59,7 @@ declare namespace Model {
         loginname: string; // 管理员名称
         loginpwd: string; // 登录密码
         memberareaid: number; // 所属会员ID
+        memberusername: string; // 所属会员
         mobile2: string; // 手机号码[明文-尚志]
         mobilephone: string; // 手机号码 (加密存储)
         modifiedby: number; // 修改人帐号
@@ -64,6 +67,7 @@ declare namespace Model {
         nickname: string; // 昵称:默认为手机号脱敏(139****9999) 或 名称脱敏(张**)
         openmode: number; // 开户方式 - 1:管理端开户 2:网上开户注册(会员官网-手机号) 3:微信开户 4:网页交易端注册 5:安卓手机端注册 6:苹果手机端注册 7:PC交易端注册 8:微信快速开户 9:支付宝快速开户 10:手机号快速开户 11:网上开户注册(会员官网-微信认证) 12:网上开户注册(会员官网-支付宝认证)
         otherurl: string; // 其它图片地址[使用分号分隔]
+        pathname: string; // 地址
         postalcode: string; // 邮政编码
         provinceid: number; // 省
         proxystatementurl: string; // 授权委托书