li.shaoyi 2 months ago
parent
commit
05ceee91b1

+ 58 - 0
src/constants/member.ts

@@ -83,4 +83,62 @@ export function getCustomerTypeList() {
 export function getCustomerTypeName(value?: number) {
     const item = getCustomerTypeList().find((e) => e.value === value)
     return item?.label ?? value
+}
+
+/**
+ * 开户验证方式
+ */
+export enum UserVerifyMethod {
+    SMS = 0, // 短信验证码
+    None = 1, // 无
+}
+
+/**
+ * 获取开户验证方式列表
+ * @returns 
+ */
+export function getUserVerifyMethodList() {
+    return [
+        { label: '无', value: UserVerifyMethod.None.toString() },
+        { label: '短信验证码', value: UserVerifyMethod.SMS.toString() },
+    ]
+}
+
+/**
+ * 获取开户验证方式名称
+ * @param value 
+ * @returns 
+ */
+export function getUserVerifyMethodName(value?: string) {
+    const item = getUserVerifyMethodList().find((e) => e.value === value)
+    return item?.label ?? value
+}
+
+/**
+ * 帐号通知方式
+ */
+export enum UserNotificationMethod {
+    SMS = 0, // 短信
+    Manual = 1, // 人工
+}
+
+/**
+ * 获取帐号通知方式列表
+ * @returns 
+ */
+export function getUserNotificationMethodList() {
+    return [
+        { label: '人工', value: UserNotificationMethod.Manual.toString() },
+        { label: '短信', value: UserNotificationMethod.SMS.toString() },
+    ]
+}
+
+/**
+ * 获取帐号通知方式名称
+ * @param value 
+ * @returns 
+ */
+export function getUserNotificationMethodName(value?: string) {
+    const item = getUserNotificationMethodList().find((e) => e.value === value)
+    return item?.label ?? value
 }

+ 112 - 0
src/packages/pc/views/base/acctopencfg/components/edit/index.vue

@@ -0,0 +1,112 @@
+<!-- 基础数据管理-网上开户配置-编辑 -->
+<template>
+    <app-drawer title="编辑" width="900" v-model:show="show" :refresh="refresh" :loading="loading">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="auto" :show-message="false">
+            <template v-for="item in formData.params" :key="item.configid">
+                <el-form-item label="开户是否审核" prop="logincode" v-if="item.configid === 4">
+                    <el-switch v-model="item.configvalue" active-value="1" inactive-value="0" />
+                </el-form-item>
+                <el-form-item label="实名是否审核" prop="password" v-if="item.configid === 64">
+                    <el-switch v-model="item.configvalue" active-value="1" inactive-value="0" />
+                </el-form-item>
+                <el-form-item label="证件反面照是否显示" prop="remark" v-if="item.configid === 53">
+                    <el-switch v-model="item.configvalue" active-value="1" inactive-value="0" />
+                </el-form-item>
+                <el-form-item :label="getConfigValue(78) + '是否显示'" prop="username" v-if="item.configid === 54">
+                    <el-switch v-model="item.configvalue" active-value="1" inactive-value="0" />
+                </el-form-item>
+                <el-form-item label="开户验证方式" prop="username" v-if="item.configid === 15">
+                    <el-select v-model="item.configvalue" :placeholder="t('common.pleasechoice')">
+                        <template v-for="item in getUserVerifyMethodList()" :key="item.value">
+                            <el-option :label="item.label" :value="item.value" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="帐号通知方式" prop="roleId" v-if="item.configid === 17">
+                    <el-select v-model="item.configvalue" :placeholder="t('common.pleasechoice')">
+                        <template v-for="item in getUserNotificationMethodList()" :key="item.value">
+                            <el-option :label="item.label" :value="item.value" />
+                        </template>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="文件上传大小" prop="username" v-if="item.configid === 25">
+                    <el-input v-model="item.configvalue" maxlength="50" :placeholder="t('common.pleaseenter')">
+                        <template #append>KB</template>
+                    </el-input>
+                </el-form-item>
+                <el-form-item label="图片存储目录" prop="username" v-if="item.configid === 6">
+                    <el-input v-model="item.configvalue" maxlength="50" :placeholder="t('common.pleaseenter')" />
+                </el-form-item>
+                <el-form-item class="el-form-item--row" label="移动端开户地址" prop="username" v-if="item.configid === 60">
+                    <el-input v-model="item.configvalue" maxlength="50" :placeholder="t('common.pleaseenter')" />
+                    <p class="g-red">*直接配置开户地址,如:http://xxxxx:xxx/wskh, 系统会自动加上"?code="</p>
+                </el-form-item>
+            </template>
+        </el-form>
+        <template #footer>
+            <el-button @click="onCancel(false)">{{ t('operation.cancel') }}</el-button>
+            <el-button type="primary" @click="onSubmit">{{ t('operation.submit') }}</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, PropType, toRaw, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import { getUserNotificationMethodList, getUserVerifyMethodList } from '@/constants/member'
+import { oaconfigAdd } from '@/services/api/base'
+import { i18n } from '@/stores'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Base.OaconfigRsp[]>,
+        required: true
+    }
+})
+
+const { global: { t } } = i18n
+const show = ref(true)
+const refresh = ref(false)
+const loading = ref(false)
+
+const formData = reactive<Base.OaconfigAddReq>({
+    params: []
+})
+
+const getConfigValue = (id: number) => {
+    const findItem = props.record.find((e) => e.configid === id)
+    return findItem?.configvalue
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    const rawData = toRaw(formData)
+    loading.value = true
+    oaconfigAdd({
+        data: rawData
+    }).then(() => {
+        ElMessage.success(t('common.tips3'))
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error(t('common.tips4') + err)
+    }).finally(() => {
+        loading.value = false
+    })
+}
+
+onMounted(() => {
+    // 自定义排序
+    const order = [4, 64, 53, 54, 15, 17, 25, 6, 60]
+    // 浅拷贝,防止数据引用
+    const params = props.record.map((e) => ({ ...e }))
+
+    formData.params = params.sort((a, b) => {
+        return order.indexOf(a.configid) - order.indexOf(b.configid)
+    })
+})
+</script>

+ 39 - 1
src/packages/pc/views/base/acctopencfg/index.vue

@@ -1,7 +1,45 @@
 <!-- 基础数据管理-网上开户配置 -->
 <template>
-    <app-view></app-view>
+    <app-view>
+        <app-operation :data-list="getActionButtons()" @click="openComponent" />
+        <app-table-details title="网上开户配置" :data="details" :cell-props="detailProps" :column="3" />
+        <component :is="componentMap.get(componentId)" v-bind="{ record }" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue'
+import { getUserNotificationMethodName, getUserVerifyMethodName } from '@/constants/member'
+import { useEnum } from '@/hooks/enum'
+import { useOperation } from '@/hooks/operation'
+import { useRequest } from '@/hooks/request'
+import { CellProp } from '@pc/components/base/table-details/types'
+import { oaconfig } from '@/services/api/base'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+import AppOperation from '@pc/components/base/operation/index.vue'
+
+const flagEnum = useEnum('flag')
+
+const { componentMap, componentId, openComponent, closeComponent, getActionButtons } = useOperation({
+    onClose: () => run()
+})
+
+const { dataList, run } = useRequest(oaconfig)
+
+const record = computed(() => dataList.value.filter((e) => [4, 6, 15, 17, 25, 53, 54, 60, 64, 78].includes(e.configid)))
+
+const details = computed(() => Object.fromEntries(record.value.map(e => [e.configid, e.configvalue])))
+
+const detailProps = computed<CellProp[]>(() => ([
+    { prop: '4', label: '开户是否审核', formatValue: (val) => flagEnum.getEnumTypeName(+val) },
+    { prop: '64', label: '实名是否审核', formatValue: (val) => flagEnum.getEnumTypeName(+val) },
+    { prop: '15', label: '开户验证方式', formatValue: (val) => getUserVerifyMethodName(val) },
+    { prop: '17', label: '帐号通知方式', formatValue: (val) => getUserNotificationMethodName(val) },
+    { prop: '53', label: '证件反面照是否显示', formatValue: (val) => flagEnum.getEnumTypeName(+val) },
+    { prop: '54', formatLabel: () => details.value['78'] + '是否显示', formatValue: (val) => flagEnum.getEnumTypeName(+val) },
+    { prop: '25', label: '文件上传大小', formatValue: (val) => val ? val + 'KB' : val },
+    { prop: '6', label: '图片存储目录' },
+    { prop: '60', label: '移动端开户地址' }
+]))
 </script>

+ 1 - 1
src/packages/pc/views/base/banner/index.vue

@@ -4,7 +4,7 @@
         <template #header>
             <app-filter :option="filterOption" />
         </template>
-        <app-table :data="dataList" showIndex :columns="tableColumns" :loading="loading">
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
             <template #headerLeft>
                 <app-operation :data-list="getActionButtons(['base_banner_add'])"
                     @click="openComponent" />

+ 105 - 0
src/packages/pc/views/base/dictionary/components/edit/index.vue

@@ -0,0 +1,105 @@
+<!-- 基础数据管理-数据字典管理-编辑 -->
+<template>
+    <app-drawer title="编辑" width="480" v-model:show="show" :refresh="refresh" :loading="loading">
+        <el-form ref="formRef" class="el-form--vertical" label-width="auto" :model="formData" :rules="formRules"
+            :show-message="false">
+            <el-form-item label="所属枚举代码" prop="enumdiccode">
+                <el-input v-model="formData.enumdiccode" maxlength="50" placeholder="请输入" />
+            </el-form-item>
+            <el-form-item label="枚举项名称" prop="enumdicname">
+                <el-input v-model="formData.enumdicname" placeholder="请输入" />
+            </el-form-item>
+            <el-form-item label="枚举项值" prop="enumitemname">
+                <el-input type="number" v-model="formData.enumitemname" placeholder="请输入" />
+            </el-form-item>
+            <el-form-item label="银行服务对应值" prop="bankmappedvalue">
+                <el-input type="number" v-model="formData.bankmappedvalue" placeholder="请输入" />
+            </el-form-item>
+            <el-form-item label="备注" prop="remark">
+                <el-input type="textarea" v-model="formData.remark" maxlength="200" :rows="3" 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, onMounted } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { useRequest } from '@/hooks/request'
+import { updateOrSave, editEnumDicItem } from '@/services/api/base'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    record: {
+        type: Object as PropType<Base.EnumDicItemByPageRsp>
+    }
+})
+
+const formRef = ref<FormInstance>()
+const show = ref(true)
+const refresh = ref(false)
+
+const formData = ref<Base.UpdateOrSaveReq>({
+    bankmappedvalue: '',
+    enumdiccode: '',
+    enumdicname: '',
+    enumitemname: 0
+})
+
+const { loading, run } = useRequest(editEnumDicItem, {
+    manual: true,
+    onSuccess: ((res) => {
+        formData.value = res.data.enumdicitem
+    }),
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+// 表单验证规则
+const formRules: FormRules = {
+    enumdiccode: [{ required: true }],
+    enumdicname: [{ required: true }],
+    enumitemname: [{ required: true }],
+    bankmappedvalue: [{ required: true }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            const rawData = toRaw(formData.value)
+            loading.value = true
+            updateOrSave({
+                data: rawData
+            }).then(() => {
+                ElMessage.success('保存成功')
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error('保存失败:' + err)
+            }).finally(() => {
+                loading.value = false
+            })
+        }
+    })
+}
+
+onMounted(() => {
+    const record = props.record
+    if (record) {
+        run({
+            autoid: record.autoid,
+            enumdiccode: record.enumdiccode,
+            ismappingtobank: record.ismappingtobank
+        })
+    }
+})
+</script>

+ 39 - 0
src/packages/pc/views/base/dictionary/components/update/index.vue

@@ -0,0 +1,39 @@
+<!-- 基础数据管理-数据字典管理-更新缓存 -->
+<template>
+    <app-drawer title="提示" v-model:show="show" :loading="loading" :refresh="refresh">
+        <div class="g-text-message">是否更新Redis缓存?</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 { refreshredis } from '@/services/api/base'
+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
+    refreshredis().then(() => {
+        ElMessage.success('更新成功')
+        onCancel(true)
+    }).catch((err) => {
+        ElMessage.error('更新失败:' + err)
+        onCancel()
+    }).finally(() => {
+        loading.value = false
+    })
+}
+</script>

+ 78 - 89
src/packages/pc/views/base/dictionary/index.vue

@@ -2,105 +2,103 @@
 <template>
     <div class="g-view-tree">
         <app-view>
-            <el-tree ref="treeRef" :data="nodeList"
-                :props="{ label: (data: { enumgroupname: string; enumdicname: string }) => data.enumgroupname || data.enumdicname }"
-                :expand-on-click-node="false" @node-click="nodeClick" highlight-current default-expand-all />
+            <el-tree ref="treeRef" :data="nodeList" node-key="id"
+                :props="{ label: (data: Tree) => data.enumdicname || data.enumgroupname }" :expand-on-click-node="false"
+                @node-click="nodeClick" highlight-current default-expand-all />
         </app-view>
         <app-view>
             <app-table :data="dataList" :columns="tableColumns">
                 <template #headerLeft>
-                    <app-operation :data-list="getActionButtons(['investor_custom_tradecfg_add'])"
-                        @click="openComponent" />
-                    <app-operation
-                        :data-list="getActionButtons(['investor_custom_tradecfg_modify', 'investor_custom_tradecfg_delete'])"
-                        @click="openComponent" v-if="data" />
+                    <span>当前分组名称:{{ selectedNode?.enumgroupname }}</span>
+                    <span v-if="selectedNode?.enumdiccode">当前枚举名称:{{ selectedNode.enumdicname }}({{
+                        selectedNode.enumdiccode }})</span>
+                </template>
+                <template #headerRight>
+                    <app-operation :data-list="getActionButtons(['base_dictionary_update'])" @click="openComponent" />
+                </template>
+                <!-- 操作 -->
+                <template #operate="{ row }">
+                    <app-operation size="small"
+                        :data-list="getActionButtons(['base_dictionary_modify', 'base_dictionary_delete'])"
+                        @click="(code: string) => openComponent(code, row)" circle />
                 </template>
             </app-table>
         </app-view>
-        <component :is="componentMap.get(componentId)" v-bind="{ record: toRaw(data) }" @closed="closeComponent"
+        <component :is="componentMap.get(componentId)" v-bind="{ record }" @closed="closeComponent"
             v-if="componentId" />
     </div>
 </template>
 
 <script lang="ts" setup>
-import { ref, computed, reactive, toRaw, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
-import type Node from 'element-plus/es/components/tree/src/model/node'
-import { buildTree, handleNoneValue } from '@/filters'
-import { Language } from '@/constants/language'
+import { ref, computed, nextTick } from 'vue'
+import { ElMessage, TreeInstance } from 'element-plus'
 import { useEnum } from '@/hooks/enum'
 import { useRequest } from '@/hooks/request'
 import { useOperation } from '@/hooks/operation'
-import { getInvestorTree, tradeConfigView } from '@/services/api/investor'
 import { queryEnumDicItemByPage, getEnumDics } from '@/services/api/base'
-import { CellProp } from '@pc/components/base/table-details/types'
 import AppTable from '@pc/components/base/table/index.vue'
 import AppOperation from '@pc/components/base/operation/index.vue'
-import AppTableDetails from '@pc/components/base/table-details/index.vue'
-import { i18n } from '@/stores'
-
-const { t } = i18n.global
-const customerTypeEnum = useEnum('customerType')
-const feetypeEnum = useEnum('feetype')
-const scfRiskMode = useEnum('SCFRiskMode')
-const traderuleEnum = useEnum('traderule') // 交易规则
-const tradefeeEnum = useEnum('tradefee') // 交易费用
 
-const treeRef = ref()
-const nodeList = ref<(Base.EnumGroup & { children: Base.EnumDic[]; })[]>([])
-const selectedNode = ref<Node>()
+interface Tree {
+    id: string;
+    enumgroupid: number;
+    enumgroupname: string;
+    enumdiccode: string;
+    enumdicname: string;
+    ismappingtobank?: number;
+    children?: Tree[];
+}
 
-const currentInfo = reactive({
-    groupName: '',
-    marketName: '',
-    goodsName: '',
-    customerType: '',
-    marginValue: ''
-})
+const flagEnum = useEnum('flag')
+const statusEnum = useEnum('status')
 
-const qs = computed<Investor.TradeConfigViewReq>(() => {
-    const data = selectedNode.value?.data
-    return {
-        usergroupid: data?.usergroupid,
-        marketid: data?.marketid,
-        goodsid: data?.goodsid,
-    }
-})
+const treeRef = ref<TreeInstance>()
+const nodeList = ref<Tree[]>([])
+const selectedNode = ref<Tree>() // 当前选中的树节点
 
-const { componentMap, componentId, openComponent, closeComponent, getActionButtons } = useOperation<Investor.TradeConfigViewRsp>({
+const { componentMap, componentId, record, openComponent, closeComponent, getActionButtons } = useOperation<Base.EnumDicItemByPageRsp>({
     onClose: (componentId) => {
-        getNodeList()
-        if (componentId === 'investor_custom_tradecfg_modify') {
-            getDetails(qs.value)
-        }
-        if (componentId === 'investor_custom_tradecfg_delete') {
-            data.value = undefined
-        }
+        console.log(componentId)
     }
 })
 
-const { run: getNodeList } = useRequest(getEnumDics, {
+useRequest(getEnumDics, {
     onSuccess: (res) => {
-        nodeList.value = res.data.enumgroups.map((e) => {
-            const children: Base.EnumDic[] = []
-            res.data.enumdics.forEach((item) => {
+        nodeList.value = res.data.enumgroups.map((e, i) => {
+            const children: Tree[] = []
+            res.data.enumdics.forEach((item, n) => {
                 if (e.enumgroupid === item.enumgroupid) {
-                    children.push(item)
+                    children.push({
+                        id: i.toString() + n.toString(),
+                        enumgroupname: e.enumgroupname,
+                        enumgroupid: item.enumgroupid,
+                        enumdiccode: item.enumdiccode,
+                        enumdicname: item.enumdicname,
+                        ismappingtobank: item.ismappingtobank
+                    })
                 }
             })
             return {
-                ...e,
-                children,
+                id: i.toString(),
+                enumgroupname: e.enumgroupname,
+                enumgroupid: e.enumgroupid,
+                enumdiccode: '',
+                enumdicname: '',
+                children
             }
         })
-    },
-    onError: (err) => {
-        ElMessage.error(err)
-    }
-})
 
-const { data, run: getDetails } = useRequest(tradeConfigView, {
-    manual: true,
+        selectedNode.value = nodeList.value[0] // 默认选中第一个节点
+
+        // 设置选中节点
+        nextTick(() => {
+            const data = selectedNode.value
+            if (data) {
+                treeRef.value?.setCurrentKey(data.id, true)
+                nodeClick(data)
+            }
+        })
+    },
     onError: (err) => {
         ElMessage.error(err)
     }
@@ -117,24 +115,23 @@ const { dataList, run: getDataList } = useRequest(queryEnumDicItemByPage, {
     }
 })
 
-const detailProps = computed<CellProp[]>(() => [
-    { prop: 'groupName', label: 'investor.custom.tradecfg.usergroupid1' },
-    { prop: 'marketName', label: 'investor.custom.tradecfg.marketid1' },
-    { prop: 'goodsName', label: 'investor.custom.tradecfg.goodsid1' },
-    { prop: 'customerType', label: 'investor.custom.tradecfg.paramid', formatValue: () => customerTypeEnum.getEnumTypeName(data.value?.paramid) },
-    { prop: 'marginValue', label: 'investor.custom.tradecfg.marketmarginvaluedisplay', formatValue: () => data.value?.marketmarginvaluedisplay },
-])
+const tableColumns = computed<Model.TableColumn[]>(() => {
+    const data = selectedNode.value ?? {}
+    return [
+        { field: 'enumdiccode', label: '所属枚举代码' },
+        { field: 'enumdicname', label: '枚举项名称' },
+        { field: 'ismappingtobank', label: '是否映射银行', formatValue: (val) => flagEnum.getEnumTypeName(val), show: !('ismappingtobank' in data) },
+        { field: 'itemstatus', label: '枚举项状态', formatValue: (val) => statusEnum.getEnumTypeName(val), show: !('ismappingtobank' in data) },
+        { field: 'enumitemname', label: '枚举项值', show: ('ismappingtobank' in data) },
+        { field: 'enumitemstatus', label: '枚举状态', formatValue: (val) => statusEnum.getEnumTypeName(val), show: ('ismappingtobank' in data) },
+        // { field: 'operate', label: 'common.operate', fixed: 'right', show: ('ismappingtobank' in data) }
+    ]
+})
 
-const tableColumns: Model.TableColumn[] = [
-    { field: 'enumdiccode', label: '所属枚举代码' },
-    { field: 'enumdicname', label: '枚举项名称' },
-    { field: 'enumitemname', label: '枚举项值' },
-    { field: 'itemstatus', label: '枚举项状态' },
-    { field: 'operate', label: 'common.operate', fixed: 'right' }
-]
+const nodeClick = (data: Tree) => {
+    selectedNode.value = data
 
-const nodeClick = (data: Base.EnumGroup | Base.EnumDic, node: Node) => {
-    if ('enumdiccode' in data) {
+    if (data.enumdiccode) {
         getDataList({
             enumgroupid: data.enumgroupid,
             enumdiccode: data.enumdiccode,
@@ -145,13 +142,5 @@ const nodeClick = (data: Base.EnumGroup | Base.EnumDic, node: Node) => {
             enumgroupid: data.enumgroupid
         })
     }
-
-    if (data.goodsid) {
-        selectedNode.value = node
-        currentInfo.goodsName = node.label
-        currentInfo.marketName = node.parent.label
-        currentInfo.groupName = node.parent.parent.label
-        getDetails(qs.value)
-    }
 }
 </script>

+ 2 - 2
src/packages/pc/views/base/errorcode/index.vue

@@ -4,7 +4,7 @@
         <template #header>
             <app-filter :option="filterOption" />
         </template>
-        <app-table :data="dataList" showIndex :columns="tableColumns" :loading="loading">
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
             <!-- 操作 -->
             <template #operate="{ row }">
                 <app-operation size="small" :data-list="getActionButtons(['base_errorcode_modify'])"
@@ -54,7 +54,7 @@ const tableColumns = ref<Model.TableColumn[]>([
     { field: 'errorcode', label: 'base.errorcode.errorcode' },
     { field: 'operatecode', label: 'base.errorcode.operatecode' },
     { field: 'description', label: 'base.errorcode.description' },
-    { field: 'operate', label: 'common.operate', fixed: 'right' }
+    // { field: 'operate', label: 'common.operate', fixed: 'right' }
 ])
 
 const { filterOption, getQueryParams, resetFilters } = useDataFilter<Base.ErrorReq>({

+ 98 - 1
src/packages/pc/views/base/region/index.vue

@@ -1,7 +1,104 @@
 <!-- 基础数据管理-地区管理 -->
 <template>
-    <app-view></app-view>
+    <div class="g-view-tree">
+        <app-view>
+            <el-tree ref="treeRef" :data="nodeList" node-key="resourceCode" :props="{ label: 'title' }"
+                :expand-on-click-node="false" @node-click="nodeClick" highlight-current />
+        </app-view>
+        <app-view>
+            <template #header>
+                <app-table-details :title="data?.divisionname" :data="data" :label-width="100" :cell-props="detailProps"
+                    :column="3" />
+            </template>
+            <app-table :data="dataList" :columns="tableColumns" />
+        </app-view>
+    </div>
 </template>
 
 <script lang="ts" setup>
+import { ref, computed } from 'vue'
+import { ElMessage, TreeInstance } from 'element-plus'
+import { findTreeNodeById } from '@/filters'
+import { useEnum } from '@/hooks/enum'
+import { useRequest } from '@/hooks/request'
+import { CellProp } from '@pc/components/base/table-details/types'
+import { getParentByAreacode, queryAreaByCondition, divisiontree } from '@/services/api/base'
+import AppTable from '@pc/components/base/table/index.vue'
+import AppTableDetails from '@pc/components/base/table-details/index.vue'
+
+const divisionlevelEnum = useEnum('divisionlevel')
+
+const treeRef = ref<TreeInstance>()
+const parentName = ref<string>() // 父级名称
+
+const { dataList: nodeList } = useRequest(divisiontree, {
+    onSuccess: (res) => {
+        const firstData = res.data[0]
+        if (treeRef.value && firstData) {
+            const firstNode = treeRef.value.getNode(firstData)
+            firstNode.expanded = true
+            treeRef.value.setCurrentKey(firstData.resourceCode, true)
+            nodeClick(firstData)
+        }
+    },
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const { data, run: getDetails } = useRequest(getParentByAreacode, {
+    manual: true,
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const { dataList, run: getDataList } = useRequest(queryAreaByCondition, {
+    manual: true,
+    onError: (err) => {
+        ElMessage.error(err)
+    }
+})
+
+const detailProps = computed<CellProp[]>(() => [
+    { prop: 'divisioncode', label: '地区代码' },
+    { prop: 'divisionname', label: '地区名称' },
+    { prop: 'pathname', label: '所属地区', formatValue: () => parentName.value },
+    { prop: 'divisionlevel', label: '行政级别', formatValue: (val) => getDivisionLevel(val) },
+    { prop: 'separablename', label: '全地址' },
+])
+
+const tableColumns = computed<Model.TableColumn[]>(() => {
+    return [
+        { field: 'divisioncode', label: '地区代码' },
+        { field: 'divisionname', label: '地区名称' },
+        { field: 'divisionlevel', label: '行政级别', formatValue: (val) => getDivisionLevel(val) },
+        { field: 'parentcode', label: '所属地区', formatValue: () => data.value?.divisionname },
+        { field: 'separablename', label: '全地址' },
+    ]
+})
+
+// 获取行政级别
+const getDivisionLevel = (value: string) => {
+    switch (value) {
+        case 'country':
+            return divisionlevelEnum.getEnumTypeName(1)
+        case 'province':
+            return divisionlevelEnum.getEnumTypeName(2)
+        case 'city':
+            return divisionlevelEnum.getEnumTypeName(3)
+        case 'district':
+            return divisionlevelEnum.getEnumTypeName(4)
+        default:
+            return value
+    }
+}
+
+const nodeClick = (data: Base.DivisionTreeRsp) => {
+    const parentNode = findTreeNodeById(nodeList.value, data.parentCode, { id: 'resourceCode' })
+    parentName.value = parentNode?.title || data.title
+
+    getDetails({ parentcode: data.resourceCode })
+    getDataList({ parentcode: data.resourceCode })
+}
 </script>

+ 1 - 1
src/packages/pc/views/base/sysparams/index.vue

@@ -1,7 +1,7 @@
 <!-- 基础数据管理-系统参数管理 -->
 <template>
     <app-view>
-        <app-table :data="dataList" showIndex :columns="tableColumns" :loading="loading">
+        <app-table :data="dataList" :columns="tableColumns" :loading="loading">
             <!-- 操作 -->
             <template #operate="{ row }">
                 <app-operation size="small" :data-list="getActionButtons(['base_sysparams_modify'])"

+ 6 - 6
src/packages/pc/views/investor/custom/accountcfg/index.vue

@@ -14,8 +14,8 @@
                     @click="openComponent" v-if="data" />
             </div>
             <template v-if="data">
-                <app-table-details :title="t('investor.custom.accountcfg.baseinfo')" :data="currentInfo" :label-width="160" :cell-props="detailProps"
-                    :column="2" />
+                <app-table-details :title="t('investor.custom.accountcfg.baseinfo')" :data="currentInfo"
+                    :label-width="160" :cell-props="detailProps" :column="2" />
                 <app-table :data="data.druleList" :columns="ruleColumns">
                     <template #headerLeft>
                         <b>{{ t('investor.custom.accountcfg.traderule') }}</b>
@@ -102,7 +102,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, computed, toRaw, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, TreeInstance } from 'element-plus'
 import type Node from 'element-plus/es/components/tree/src/model/node'
 import { handleNoneValue, buildTree } from '@/filters'
 import { useEnum } from '@/hooks/enum'
@@ -123,9 +123,9 @@ const tradefee = useEnum('tradefee')
 
 const { global: { t } } = i18n
 
-const treeRef = ref()
+const treeRef = ref<TreeInstance>()
 const nodeList = ref<(Investor.AccTradeTreeRsp & { children: Investor.AccTradeTreeRsp[]; })[]>([])
-const selectedNode = ref<Node>()
+const selectedNode = ref<Node>() // 当前选中的树节点
 
 const currentInfo = reactive({
     userName: '',
@@ -178,7 +178,7 @@ const { run: getNodeList } = useRequest(getAccTradeTree, {
         nextTick(() => {
             const data = selectedNode.value?.data
             if (data) {
-                treeRef.value.setCurrentKey(data.id)
+                treeRef.value?.setCurrentKey(data.id)
             }
         })
     },

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

@@ -84,7 +84,7 @@
 
 <script lang="ts" setup>
 import { ref, computed, reactive, toRaw, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, TreeInstance } from 'element-plus'
 import type Node from 'element-plus/es/components/tree/src/model/node'
 import { buildTree, handleNoneValue } from '@/filters'
 import { Language } from '@/constants/language'
@@ -121,9 +121,9 @@ const scfRiskMode = useEnum('SCFRiskMode')
 const traderuleEnum = useEnum('traderule') // 交易规则
 const tradefeeEnum = useEnum('tradefee') // 交易费用
 
-const treeRef = ref()
+const treeRef = ref<TreeInstance>()
 const nodeList = ref<(Investor.InvestorTreeRsp & { children: Investor.InvestorTreeRsp[]; })[]>([])
-const selectedNode = ref<Node>()
+const selectedNode = ref<Node>() // 当前选中的树节点
 
 const currentInfo = reactive({
     groupName: '',
@@ -177,7 +177,7 @@ const { run: getNodeList } = useRequest(getInvestorTree, {
         nextTick(() => {
             const data = selectedNode.value?.data
             if (data) {
-                treeRef.value.setCurrentKey(data.id)
+                treeRef.value?.setCurrentKey(data.id)
             }
         })
     },

+ 1 - 1
src/packages/pc/views/member/institution/tradecfg/components/add/index.vue

@@ -108,7 +108,7 @@ import AppTable from '@pc/components/base/table/index.vue'
 import AppSelectMember from '@pc/components/modules/select-member/index.vue'
 
 // 获取本地化属性键
-const getLocalizedKey: () => keyof Investor.InvestorPersonRsp['goods'][number] = () => {
+const getLocalizedKey: () => keyof Member.LoadSelectOptionRsp['goods'][number] = () => {
     switch (i18n.global.locale) {
         case Language.Simplified:
             return 'goodsname'

+ 6 - 6
src/packages/pc/views/member/institution/tradecfg/index.vue

@@ -14,8 +14,8 @@
                     @click="openComponent" v-if="data" />
             </div>
             <template v-if="data">
-                <app-table-details :title="t('member.institution.tradecfg.subtitle')" :data="currentInfo" :label-width="160" :cell-props="detailProps"
-                    :column="2" />
+                <app-table-details :title="t('member.institution.tradecfg.subtitle')" :data="currentInfo"
+                    :label-width="160" :cell-props="detailProps" :column="2" />
                 <app-table :data="data.druleList" :columns="ruleColumns">
                     <template #headerLeft>
                         <b>{{ t('member.institution.tradecfg.traderule') }}</b>
@@ -68,7 +68,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, computed, toRaw, nextTick } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, TreeInstance } from 'element-plus'
 import type Node from 'element-plus/es/components/tree/src/model/node'
 import { handleNoneValue, buildTree } from '@/filters'
 import { useEnum } from '@/hooks/enum'
@@ -89,9 +89,9 @@ const scfRiskMode = useEnum('SCFRiskMode')
 const traderule = useEnum('traderule')
 const tradefee = useEnum('tradefee')
 
-const treeRef = ref()
+const treeRef = ref<TreeInstance>()
 const nodeList = ref<(Member.CreateTreeRsp & { children: Member.CreateTreeRsp[]; })[]>([])
-const selectedNode = ref<Node>()
+const selectedNode = ref<Node>() // 当前选中的树节点
 
 const currentInfo = reactive({
     userName: '',
@@ -156,7 +156,7 @@ const { run: getNodeList } = useRequest(createTree, {
         nextTick(() => {
             const data = selectedNode.value?.data
             if (data) {
-                treeRef.value.setCurrentKey(data.id)
+                treeRef.value?.setCurrentKey(data.id)
             }
         })
     },

+ 45 - 10
src/services/api/base/index.ts

@@ -4,28 +4,28 @@ import { CommonFetchOptions } from '@/services/http/types'
 /**
  * 基础数据管理-->错误码管理-->获取列表
  */
-export function queryError(options: CommonFetchOptions<{ request: Base.ErrorReq, response: Base.ErrorRsp[]; }> = {}) {
+export function queryError(options: CommonFetchOptions<{ request: Base.ErrorReq; response: Base.ErrorRsp[]; }> = {}) {
     return httpClient.commonRequest('/setup/queryErr', 'get', options)
 }
 
 /**
  * 基础数据管理-->市场板块设置-->获取列表
  */
-export function queryMarketSet(options: CommonFetchOptions<{ request: Base.MarketSetReq, response: Base.MarketSetRsp[]; }> = {}) {
+export function queryMarketSet(options: CommonFetchOptions<{ request: Base.MarketSetReq; response: Base.MarketSetRsp[]; }> = {}) {
     return httpClient.commonRequest('/setup/queryMarketSet', 'get', options)
 }
 
 /**
  * 基础数据管理-->系统参数管理-->获取列表
  */
-export function queryParam(options: CommonFetchOptions<{ request: Base.ParamReq, response: Base.ParamRsp[]; }> = {}) {
+export function queryParam(options: CommonFetchOptions<{ request: Base.ParamReq; response: Base.ParamRsp[]; }> = {}) {
     return httpClient.commonRequest('/setup/queryParam', 'get', options)
 }
 
 /**
  * 基础数据管理-->终端图片管理-->获取列表
  */
-export function querypicture(options: CommonFetchOptions<{ request: Base.PictureReq, response: Base.PictureRsp[]; }> = {}) {
+export function querypicture(options: CommonFetchOptions<{ request: Base.PictureReq; response: Base.PictureRsp[]; }> = {}) {
     return httpClient.commonRequest('/setup/querypicture', 'get', options)
 }
 
@@ -39,14 +39,14 @@ export function querytrademodes(options: CommonFetchOptions<{ response: Member.M
 /**
  * 基础数据管理-->终端图片管理-->详情
  */
-export function pictureDetail(options: CommonFetchOptions<{ request: Base.PictureDetailReq, response: Base.PictureDetailRsp; }> = {}) {
+export function pictureDetail(options: CommonFetchOptions<{ request: Base.PictureDetailReq; response: Base.PictureDetailRsp; }> = {}) {
     return httpClient.commonRequest('/setup/pictureDetail', 'get', options)
 }
 
 /**
  * 基础数据管理-->市场板块设置-->详情
  */
-export function marketSetDetail(options: CommonFetchOptions<{ request: Base.MarketSetDetailReq, response: Base.MarketSetDetailRsp; }> = {}) {
+export function marketSetDetail(options: CommonFetchOptions<{ request: Base.MarketSetDetailReq; response: Base.MarketSetDetailRsp; }> = {}) {
     return httpClient.commonRequest('/setup/marketSetDetail', 'get', options)
 }
 
@@ -102,28 +102,28 @@ export function getEnumDics(options: CommonFetchOptions<{ response: Base.EnumDic
 /**
  * 基础数据管理-->数据字典管理-->获取列表
  */
-export function queryEnumDicItemByPage(options: CommonFetchOptions<{ request: Base.EnumDicItemByPageReq, response: Base.EnumDicItemByPageRsp[]; }>) {
+export function queryEnumDicItemByPage(options: CommonFetchOptions<{ request: Base.EnumDicItemByPageReq; response: Base.EnumDicItemByPageRsp[]; }>) {
     return httpClient.commonRequest('/setup/queryEnumDicItemByPage', 'get', options)
 }
 
 /**
  * 基础数据管理-->数据字典管理-->获取详情(二级)
  */
-export function editEnumDic(options: CommonFetchOptions<{ request: Base.EditEnumDicReq, response: Base.EditEnumDicRsp; }>) {
+export function editEnumDic(options: CommonFetchOptions<{ request: Base.EditEnumDicReq; response: Base.EditEnumDicRsp; }>) {
     return httpClient.commonRequest('/setup/editEnumDic', 'get', options)
 }
 
 /**
  * 基础数据管理-->数据字典管理-->获取详情(三级)
  */
-export function editEnumDicItem(options: CommonFetchOptions<{ request: Base.EditEnumDicItemReq, response: Base.EditEnumDicItemRsp; }>) {
+export function editEnumDicItem(options: CommonFetchOptions<{ request: Base.EditEnumDicItemReq; response: Base.EditEnumDicItemRsp; }>) {
     return httpClient.commonRequest('/setup/editEnumDicItem', 'get', options)
 }
 
 /**
  * 基础数据管理-->数据字典管理-->更新缓存
  */
-export function refreshredis(options: CommonFetchOptions) {
+export function refreshredis(options: CommonFetchOptions = {}) {
     return httpClient.commonRequest('/setup/refreshredis', 'get', options)
 }
 
@@ -146,4 +146,39 @@ export function updateOrSave(options: CommonFetchOptions<{ request: Base.UpdateO
  */
 export function delEnumdicItem(options: CommonFetchOptions<{ request: Base.DelEnumdicItemReq }>) {
     return httpClient.commonRequest('/setup/delEnumdicItem', 'get', options)
+}
+
+/**
+ * 基础数据管理-->行政区域管理-->获取菜单树
+ */
+export function divisiontree(options: CommonFetchOptions<{ response: Base.DivisionTreeRsp[]; }>) {
+    return httpClient.commonRequest('/setup/divisiontree', 'get', options)
+}
+
+/**
+ * 基础数据管理-->行政区域管理-->获取地区信息
+ */
+export function getParentByAreacode(options: CommonFetchOptions<{ request: Base.ParentByAreacodeReq; response: Base.ParentByAreacodeRsp; }>) {
+    return httpClient.commonRequest('/setup/getParentByAreacode', 'get', options)
+}
+
+/**
+ * 基础数据管理-->行政区域管理-->获取详情
+ */
+export function queryAreaByCondition(options: CommonFetchOptions<{ request: Base.ParentByAreacodeReq; response: Base.ParentByAreacodeRsp[]; }>) {
+    return httpClient.commonRequest('/setup/queryAreaByCondition', 'get', options)
+}
+
+/**
+ * 基础数据管理-->网上开户配置-->详情
+ */
+export function oaconfig(options: CommonFetchOptions<{ response: Base.OaconfigRsp[]; }>) {
+    return httpClient.commonRequest('/setup/oaconfig', 'get', options)
+}
+
+/**
+ * 基础数据管理-->网上开户配置-->保存
+ */
+export function oaconfigAdd(options: CommonFetchOptions<{ request: Base.OaconfigAddReq }>) {
+    return httpClient.commonRequest('/setup/oaconfigAdd', 'post', options)
 }

+ 65 - 16
src/types/model/base.d.ts

@@ -389,7 +389,7 @@ declare namespace Base {
     /** 基础数据管理-->数据字典管理-->获取详情(三级) 请求 */
     interface EditEnumDicItemReq {
         autoid: number;
-        enumdiccode: number;
+        enumdiccode: string;
         ismappingtobank: number;
     }
 
@@ -423,29 +423,29 @@ declare namespace Base {
 
     /** 基础数据管理-->数据字典管理-->新增(二级) 请求 */
     interface SaveEnumDicReq {
-        autoid: number; // 自增ID
+        autoid?: number; // 自增ID
         enumdiccode: string; // 枚举代码
         enumdicname: string; // 枚举名称
         enumgroupid: number; // 所属枚举分组ID
         ismappingtobank: number; // 是否映射到银行服务 - 0:不映射 1:映射
-        itemmaintaintype: number; // 子项维护类型 - 1:不可维护 2:系统项内维护 3:客户自主维护 4:初始化维护(如交易模式,交易属性)
-        itemstatus: number; // 枚举项状态 - 1.启用 2.不启用
-        modifyflag: number; // 修改标志 - 0:不允许修改 1:允许修改
-        remark: string; // 备注
-        showflag: number; // 是否显示 - 0:不显示 1:显示
+        itemmaintaintype?: number; // 子项维护类型 - 1:不可维护 2:系统项内维护 3:客户自主维护 4:初始化维护(如交易模式,交易属性)
+        itemstatus?: number; // 枚举项状态 - 1.启用 2.不启用
+        modifyflag?: number; // 修改标志 - 0:不允许修改 1:允许修改
+        remark?: string; // 备注
+        showflag?: number; // 是否显示 - 0:不显示 1:显示
     }
 
     /** 基础数据管理-->数据字典管理-->新增(三级) 请求 */
     interface UpdateOrSaveReq {
-        autoid: number;
-        bankmappedvalue: string;
-        cardlength: number;
-        enumdiccode: string;
-        enumdicname: string;
-        enumitemname: number;
-        isperson: number;
-        regexpress: string;
-        remark: string;
+        autoid?: number;
+        bankmappedvalue: string; // 银行服务对应值
+        cardlength?: number;
+        enumdiccode: string; // 所属枚举代码
+        enumdicname: string; // 枚举项名称
+        enumitemname: number; // 枚举项值
+        isperson?: number;
+        regexpress?: string;
+        remark?: string; // 备注
     }
 
     /** 基础数据管理-->数据字典管理-->删除 请求 */
@@ -453,4 +453,53 @@ declare namespace Base {
         autoid: number;
         flag: number;
     }
+
+    /** 基础数据管理-->行政区域管理-->获取菜单树 响应 */
+    interface DivisionTreeRsp {
+        authType: number; // 权限类型 - 1:菜单 2:组件 3:按钮
+        children: DivisionTreeRsp[];
+        className: string; // 菜单样式
+        component: string; // 组件名或组件地址
+        icon: string; // 菜单图标
+        parentCode: string; // 父ID
+        pathname: string; // 地区菜单用
+        remark: string; // 备注
+        resourceCode: string; // 菜单编码
+        show: number; // 是否隐藏 - 0:隐藏 1:显示
+        sort: number; // 排序
+        title: string; // 标题
+        url: string; // 链接地址
+        urlType: number; // 地址类型 - 1:路由 2:外链 3:内联框架
+    }
+
+    /** 基础数据管理-->行政区域管理-->获取地区信息 请求 */
+    interface ParentByAreacodeReq {
+        parentcode: string;
+    }
+
+    /** 基础数据管理-->行政区域管理-->获取地区信息 响应 */
+    interface ParentByAreacodeRsp {
+        autoid: number; // 自增ID
+        divisioncode: string; // 行政代码
+        divisionlevel: string; // 行政级别
+        divisionname: string; // 行政名称
+        modifierid: number; // 修改人
+        modifytime: string; // 修改时间
+        parentcode: string; // 上级行政代码
+        pathname: string; // 路径名称
+        postcode: string; // 邮政编码
+        separablename: string; // 可拆分的全称
+        shortcode: string; // 地区简码
+    }
+
+    /** 基础数据管理-->网上开户配置-->详情 响应 */
+    interface OaconfigRsp {
+        configid: number;
+        configvalue: string;
+    }
+
+    /** 基础数据管理-->网上开户配置-->保存 请求 */
+    interface OaconfigAddReq {
+        params: OaconfigRsp[]
+    }
 }

+ 5 - 1
src/types/model/member.d.ts

@@ -1881,6 +1881,10 @@ declare namespace Member {
             goodsgroupid: number; // 期货品种ID
             goodsid: number; // 期货合约ID(自增ID SEQ_GOODS)
             goodsname: string; // 期货合约名称
+            goodsnameen: string;
+            goodsnameth: string;
+            goodsnametw: string;
+            goodsnamevi: string;
             goodsquotetype: number; // 合约报价类型: 1-直接报价 2-间接报价
             goodsstatus: number; // 商品状态- 1:待审核 2:未上市 3:上市 4:已注销 5:审核拒绝 6:退市 7:待退市
             goodstradetype: number; // 商品交易权限类型 - 1:可建可平 2:可建不可平 3:不可建可平 4:不可建不可平
@@ -2095,7 +2099,7 @@ declare namespace Member {
         // 期货合约乘数
         agreeunit: number
         // 所属机构
-        areauserid: number 
+        areauserid: number
         // 审核操作员账号
         auditaccountid: number
         // 审核时间