feat: 新增代币化管理功能,包括代币化产品的添加、编辑和列表展示
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# backend service base url, test environment
|
# backend service base url, test environment
|
||||||
VITE_SERVICE_BASE_URL=http://192.168.1.6:9528
|
VITE_SERVICE_BASE_URL=http://192.168.1.17:9528
|
||||||
|
|
||||||
# other backend service base url, test environment
|
# other backend service base url, test environment
|
||||||
VITE_OTHER_SERVICE_BASE_URL= `{}`
|
VITE_OTHER_SERVICE_BASE_URL= `{}`
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
"@better-scroll/core": "2.5.1",
|
"@better-scroll/core": "2.5.1",
|
||||||
"@elysiajs/eden": "^1.4.5",
|
"@elysiajs/eden": "^1.4.5",
|
||||||
"@iconify/vue": "5.0.0",
|
"@iconify/vue": "5.0.0",
|
||||||
"@riwa/api-types": "http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz",
|
"@riwa/api-types": "http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz",
|
||||||
"@sa/axios": "workspace:*",
|
"@sa/axios": "workspace:*",
|
||||||
"@sa/color": "workspace:*",
|
"@sa/color": "workspace:*",
|
||||||
"@sa/hooks": "workspace:*",
|
"@sa/hooks": "workspace:*",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -18,8 +18,8 @@ importers:
|
|||||||
specifier: 5.0.0
|
specifier: 5.0.0
|
||||||
version: 5.0.0(vue@3.5.25(typescript@5.9.3))
|
version: 5.0.0(vue@3.5.25(typescript@5.9.3))
|
||||||
'@riwa/api-types':
|
'@riwa/api-types':
|
||||||
specifier: http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz
|
specifier: http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz
|
||||||
version: http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
version: '@riwa/eden@http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))'
|
||||||
'@sa/axios':
|
'@sa/axios':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:packages/axios
|
version: link:packages/axios
|
||||||
@@ -1083,9 +1083,9 @@ packages:
|
|||||||
'@quansync/fs@0.1.6':
|
'@quansync/fs@0.1.6':
|
||||||
resolution: {integrity: sha512-zoA8SqQO11qH9H8FCBR7NIbowYARIPmBz3nKjgAaOUDi/xPAAu1uAgebtV7KXHTc6CDZJVRZ1u4wIGvY5CWYaw==}
|
resolution: {integrity: sha512-zoA8SqQO11qH9H8FCBR7NIbowYARIPmBz3nKjgAaOUDi/xPAAu1uAgebtV7KXHTc6CDZJVRZ1u4wIGvY5CWYaw==}
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz':
|
'@riwa/eden@http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz':
|
||||||
resolution: {tarball: http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz}
|
resolution: {tarball: http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz}
|
||||||
version: 0.0.74
|
version: 0.0.81
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@elysiajs/eden': ^1.4.5
|
'@elysiajs/eden': ^1.4.5
|
||||||
|
|
||||||
@@ -5080,7 +5080,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
quansync: 0.3.0
|
quansync: 0.3.0
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.8:9527/api/riwa-api-types-0.0.74.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
'@riwa/eden@http://192.168.1.17:9527/api/riwa-eden-0.0.81.tgz(@elysiajs/eden@1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@elysiajs/eden': 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
'@elysiajs/eden': 1.4.5(elysia@1.4.19(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,9 @@ const local: App.I18n.Schema = {
|
|||||||
transfer: 'Transfer',
|
transfer: 'Transfer',
|
||||||
withdraw_approved: 'Approved Withdraw',
|
withdraw_approved: 'Approved Withdraw',
|
||||||
asset: 'Asset Management',
|
asset: 'Asset Management',
|
||||||
tradingpairs: 'Trading Pairs Management'
|
tradingpairs: 'Trading Pairs Management',
|
||||||
|
tokenization: 'Tokenization Management',
|
||||||
|
tokenization_product: 'Tokenization Product'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
|||||||
@@ -241,7 +241,9 @@ const local: App.I18n.Schema = {
|
|||||||
user_transfer: '用户转账记录',
|
user_transfer: '用户转账记录',
|
||||||
transfer: '转账管理',
|
transfer: '转账管理',
|
||||||
asset: '资产管理',
|
asset: '资产管理',
|
||||||
tradingpairs: '交易对管理'
|
tradingpairs: '交易对管理',
|
||||||
|
tokenization: '代币化管理',
|
||||||
|
tokenization_product: '代币化产品'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
rwa_product: () => import("@/views/rwa/product/index.vue"),
|
rwa_product: () => import("@/views/rwa/product/index.vue"),
|
||||||
rwa_producttype: () => import("@/views/rwa/productType/index.vue"),
|
rwa_producttype: () => import("@/views/rwa/productType/index.vue"),
|
||||||
rwa_subscribe: () => import("@/views/rwa/subscribe/index.vue"),
|
rwa_subscribe: () => import("@/views/rwa/subscribe/index.vue"),
|
||||||
|
tokenization_product: () => import("@/views/tokenization/product/index.vue"),
|
||||||
tradingpairs: () => import("@/views/tradingPairs/index.vue"),
|
tradingpairs: () => import("@/views/tradingPairs/index.vue"),
|
||||||
transfer: () => import("@/views/transfer/index.vue"),
|
transfer: () => import("@/views/transfer/index.vue"),
|
||||||
user_bank: () => import("@/views/user/bank/index.vue"),
|
user_bank: () => import("@/views/user/bank/index.vue"),
|
||||||
|
|||||||
@@ -39,6 +39,17 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
hideInMenu: true
|
hideInMenu: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'home',
|
||||||
|
path: '/home',
|
||||||
|
component: 'layout.base$view.home',
|
||||||
|
meta: {
|
||||||
|
title: 'home',
|
||||||
|
i18nKey: 'route.home',
|
||||||
|
icon: 'mdi:monitor-dashboard',
|
||||||
|
order: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'asset',
|
name: 'asset',
|
||||||
path: '/asset',
|
path: '/asset',
|
||||||
@@ -46,8 +57,66 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'asset',
|
title: 'asset',
|
||||||
i18nKey: 'route.asset',
|
i18nKey: 'route.asset',
|
||||||
order: 5
|
order: 2
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'transfer',
|
||||||
|
path: '/transfer',
|
||||||
|
component: 'layout.base$view.transfer',
|
||||||
|
meta: {
|
||||||
|
title: 'transfer',
|
||||||
|
i18nKey: 'route.transfer',
|
||||||
|
order: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user',
|
||||||
|
path: '/user',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: 'user',
|
||||||
|
i18nKey: 'route.user',
|
||||||
|
order: 4
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'user_bank',
|
||||||
|
path: '/user/bank',
|
||||||
|
component: 'view.user_bank',
|
||||||
|
meta: {
|
||||||
|
title: 'user_bank',
|
||||||
|
i18nKey: 'route.user_bank'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user_bankcard',
|
||||||
|
path: '/user/bankcard',
|
||||||
|
component: 'view.user_bankcard',
|
||||||
|
meta: {
|
||||||
|
title: 'user_bankcard',
|
||||||
|
i18nKey: 'route.user_bankcard'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user_list',
|
||||||
|
path: '/user/list',
|
||||||
|
component: 'view.user_list',
|
||||||
|
meta: {
|
||||||
|
title: 'user_list',
|
||||||
|
i18nKey: 'route.user_list'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user_transfer',
|
||||||
|
path: '/user/transfer',
|
||||||
|
component: 'view.user_transfer',
|
||||||
|
meta: {
|
||||||
|
title: 'user_transfer',
|
||||||
|
i18nKey: 'route.user_transfer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'deposit',
|
name: 'deposit',
|
||||||
@@ -56,7 +125,7 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'deposit',
|
title: 'deposit',
|
||||||
i18nKey: 'route.deposit',
|
i18nKey: 'route.deposit',
|
||||||
order: 2
|
order: 5
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -70,17 +139,6 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'home',
|
|
||||||
path: '/home',
|
|
||||||
component: 'layout.base$view.home',
|
|
||||||
meta: {
|
|
||||||
title: 'home',
|
|
||||||
i18nKey: 'route.home',
|
|
||||||
icon: 'mdi:monitor-dashboard',
|
|
||||||
order: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'iframe-page',
|
name: 'iframe-page',
|
||||||
path: '/iframe-page/:url',
|
path: '/iframe-page/:url',
|
||||||
@@ -145,6 +203,27 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'tokenization',
|
||||||
|
path: '/tokenization',
|
||||||
|
component: 'layout.base',
|
||||||
|
meta: {
|
||||||
|
title: '代币化管理',
|
||||||
|
i18nKey: 'route.tokenization',
|
||||||
|
order: 6
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'tokenization_product',
|
||||||
|
path: '/tokenization/product',
|
||||||
|
component: 'view.tokenization_product',
|
||||||
|
meta: {
|
||||||
|
title: '代币化产品',
|
||||||
|
i18nKey: 'route.tokenization_product'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'tradingpairs',
|
name: 'tradingpairs',
|
||||||
path: '/tradingpairs',
|
path: '/tradingpairs',
|
||||||
@@ -152,67 +231,11 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'tradingpairs',
|
title: 'tradingpairs',
|
||||||
i18nKey: 'route.tradingpairs',
|
i18nKey: 'route.tradingpairs',
|
||||||
order: 6
|
order: 7
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'transfer',
|
|
||||||
path: '/transfer',
|
|
||||||
component: 'layout.base$view.transfer',
|
|
||||||
meta: {
|
|
||||||
title: 'transfer',
|
|
||||||
i18nKey: 'route.transfer',
|
|
||||||
order: 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'user',
|
|
||||||
path: '/user',
|
|
||||||
component: 'layout.base',
|
|
||||||
meta: {
|
|
||||||
title: 'user',
|
|
||||||
i18nKey: 'route.user',
|
|
||||||
order: 2
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'user_bank',
|
|
||||||
path: '/user/bank',
|
|
||||||
component: 'view.user_bank',
|
|
||||||
meta: {
|
|
||||||
title: 'user_bank',
|
|
||||||
i18nKey: 'route.user_bank'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'user_bankcard',
|
|
||||||
path: '/user/bankcard',
|
|
||||||
component: 'view.user_bankcard',
|
|
||||||
meta: {
|
|
||||||
title: 'user_bankcard',
|
|
||||||
i18nKey: 'route.user_bankcard'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'user_list',
|
|
||||||
path: '/user/list',
|
|
||||||
component: 'view.user_list',
|
|
||||||
meta: {
|
|
||||||
title: 'user_list',
|
|
||||||
i18nKey: 'route.user_list'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'user_transfer',
|
|
||||||
path: '/user/transfer',
|
|
||||||
component: 'view.user_transfer',
|
|
||||||
meta: {
|
|
||||||
title: 'user_transfer',
|
|
||||||
i18nKey: 'route.user_transfer'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'withdraw',
|
name: 'withdraw',
|
||||||
path: '/withdraw',
|
path: '/withdraw',
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ const routeMap: RouteMap = {
|
|||||||
"rwa_product": "/rwa/product",
|
"rwa_product": "/rwa/product",
|
||||||
"rwa_producttype": "/rwa/producttype",
|
"rwa_producttype": "/rwa/producttype",
|
||||||
"rwa_subscribe": "/rwa/subscribe",
|
"rwa_subscribe": "/rwa/subscribe",
|
||||||
|
"tokenization": "/tokenization",
|
||||||
|
"tokenization_product": "/tokenization/product",
|
||||||
"tradingpairs": "/tradingpairs",
|
"tradingpairs": "/tradingpairs",
|
||||||
"transfer": "/transfer",
|
"transfer": "/transfer",
|
||||||
"user": "/user",
|
"user": "/user",
|
||||||
|
|||||||
4
src/typings/elegant-router.d.ts
vendored
4
src/typings/elegant-router.d.ts
vendored
@@ -30,6 +30,8 @@ declare module "@elegant-router/types" {
|
|||||||
"rwa_product": "/rwa/product";
|
"rwa_product": "/rwa/product";
|
||||||
"rwa_producttype": "/rwa/producttype";
|
"rwa_producttype": "/rwa/producttype";
|
||||||
"rwa_subscribe": "/rwa/subscribe";
|
"rwa_subscribe": "/rwa/subscribe";
|
||||||
|
"tokenization": "/tokenization";
|
||||||
|
"tokenization_product": "/tokenization/product";
|
||||||
"tradingpairs": "/tradingpairs";
|
"tradingpairs": "/tradingpairs";
|
||||||
"transfer": "/transfer";
|
"transfer": "/transfer";
|
||||||
"user": "/user";
|
"user": "/user";
|
||||||
@@ -80,6 +82,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "iframe-page"
|
| "iframe-page"
|
||||||
| "login"
|
| "login"
|
||||||
| "rwa"
|
| "rwa"
|
||||||
|
| "tokenization"
|
||||||
| "tradingpairs"
|
| "tradingpairs"
|
||||||
| "transfer"
|
| "transfer"
|
||||||
| "user"
|
| "user"
|
||||||
@@ -111,6 +114,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "rwa_product"
|
| "rwa_product"
|
||||||
| "rwa_producttype"
|
| "rwa_producttype"
|
||||||
| "rwa_subscribe"
|
| "rwa_subscribe"
|
||||||
|
| "tokenization_product"
|
||||||
| "tradingpairs"
|
| "tradingpairs"
|
||||||
| "transfer"
|
| "transfer"
|
||||||
| "user_bank"
|
| "user_bank"
|
||||||
|
|||||||
214
src/views/rwa/product/components/tokenization.vue
Normal file
214
src/views/rwa/product/components/tokenization.vue
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, useTemplateRef } from 'vue';
|
||||||
|
import { type FormInst, type FormRules, NInput, useDialog } from 'naive-ui';
|
||||||
|
import type { Treaty } from '@elysiajs/eden';
|
||||||
|
import { client, safeClient } from '@/service/api';
|
||||||
|
|
||||||
|
defineOptions({ name: 'RwaProductTokenization' });
|
||||||
|
|
||||||
|
type Data = Treaty.Data<typeof client.api.admin.rwa.issuance.products.get>['data'][number];
|
||||||
|
type Body = CommonType.TreatyBody<typeof client.api.admin.rwa.tokenization_schema.issue.post>;
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: Data;
|
||||||
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const formRef = useTemplateRef<FormInst | null>('formRef');
|
||||||
|
|
||||||
|
const form = ref<Body>({
|
||||||
|
productId: props.data.id,
|
||||||
|
assetCode: props.data.code,
|
||||||
|
totalSupply: '100',
|
||||||
|
lockOptions: [
|
||||||
|
{
|
||||||
|
months: 6,
|
||||||
|
rewardRate: '0.05' // 奖励率(如 0.05 = 5%)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
assetCode: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择资产代码',
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
totalSupply: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入发行总量',
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (_rule, value) => {
|
||||||
|
if (Number(value) < 1) {
|
||||||
|
return new Error('发行总量必须大于0');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function addLockOption() {
|
||||||
|
(form.value.lockOptions ||= []).push({
|
||||||
|
months: 12,
|
||||||
|
rewardRate: '0.1'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLockOption(index: number) {
|
||||||
|
if (form.value.lockOptions && form.value.lockOptions.length > 1) {
|
||||||
|
form.value.lockOptions.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
formRef.value?.validate(async errors => {
|
||||||
|
if (!errors) {
|
||||||
|
await safeClient(() =>
|
||||||
|
client.api.admin.rwa.tokenization_schema.issue.post({
|
||||||
|
...form.value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
window.$message?.success('代币化产品发行请求已提交。');
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NForm
|
||||||
|
ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="auto"
|
||||||
|
label-placement="left"
|
||||||
|
require-mark-placement="left"
|
||||||
|
>
|
||||||
|
<NGrid :cols="1">
|
||||||
|
<NFormItemGi label="资产代码" path="assetCode">
|
||||||
|
<NInput v-model:value="form.assetCode" placeholder="请输入资产代码" />
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi label="发行总量" path="totalSupply">
|
||||||
|
<NInputNumber
|
||||||
|
:min="1"
|
||||||
|
:step="100"
|
||||||
|
:value="Number(form.totalSupply)"
|
||||||
|
@update:value="val => (form.totalSupply = String(val))"
|
||||||
|
/>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
|
||||||
|
<NDivider />
|
||||||
|
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<span class="text-base font-medium">锁仓选项</span>
|
||||||
|
<NButton secondary type="primary" @click="addLockOption">
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-plus class="text-icon" />
|
||||||
|
</template>
|
||||||
|
添加选项
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<NSpace vertical :size="16">
|
||||||
|
<NCard
|
||||||
|
v-for="(option, index) in form.lockOptions"
|
||||||
|
:key="index"
|
||||||
|
size="small"
|
||||||
|
:segmented="{ content: true }"
|
||||||
|
class="rounded-8px"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>选项 {{ index + 1 }}</span>
|
||||||
|
<NButton
|
||||||
|
v-if="form.lockOptions && form.lockOptions.length > 1"
|
||||||
|
text
|
||||||
|
type="error"
|
||||||
|
@click="removeLockOption(index)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-ic-round-delete class="text-icon" />
|
||||||
|
</template>
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<NGrid :cols="2" :x-gap="16">
|
||||||
|
<NFormItemGi
|
||||||
|
label="锁仓月数"
|
||||||
|
:path="`lockOptions[${index}].months`"
|
||||||
|
:rule="{
|
||||||
|
required: true,
|
||||||
|
type: 'number',
|
||||||
|
message: '请输入锁仓月数',
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<NInputNumber
|
||||||
|
v-model:value="option.months"
|
||||||
|
:min="1"
|
||||||
|
:max="120"
|
||||||
|
:step="1"
|
||||||
|
placeholder="请输入锁仓月数"
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<template #suffix>月</template>
|
||||||
|
</NInputNumber>
|
||||||
|
</NFormItemGi>
|
||||||
|
<NFormItemGi
|
||||||
|
label="奖励率"
|
||||||
|
:path="`lockOptions[${index}].rewardRate`"
|
||||||
|
:rule="{
|
||||||
|
required: true,
|
||||||
|
validator: (_rule: any, value: string) => {
|
||||||
|
const num = Number(value);
|
||||||
|
if (!value || value === '') {
|
||||||
|
return new Error('请输入奖励率');
|
||||||
|
}
|
||||||
|
if (Number.isNaN(num)) {
|
||||||
|
return new Error('请输入有效的数字');
|
||||||
|
}
|
||||||
|
if (num < 0 || num > 1) {
|
||||||
|
return new Error('奖励率必须在0-1之间');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<NInputNumber
|
||||||
|
:value="Number(option.rewardRate)"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:step="0.01"
|
||||||
|
:precision="4"
|
||||||
|
placeholder="请输入奖励率"
|
||||||
|
class="w-full"
|
||||||
|
@update:value="val => (option.rewardRate = String(val))"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<span class="text-12px">{{ (Number(option.rewardRate) * 100).toFixed(2) }}%</span>
|
||||||
|
</template>
|
||||||
|
</NInputNumber>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
|
||||||
|
<NSpace justify="end" class="mt-4">
|
||||||
|
<NButton @click="$emit('close')">取消</NButton>
|
||||||
|
<NButton type="primary" @click="handleSubmit">立即代币化</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped></style>
|
||||||
@@ -8,6 +8,7 @@ import { RwaStatusEnum } from '@/enum';
|
|||||||
import Add from './components/add.vue';
|
import Add from './components/add.vue';
|
||||||
import Edit from './components/edit.vue';
|
import Edit from './components/edit.vue';
|
||||||
import Editions from './components/editions.vue';
|
import Editions from './components/editions.vue';
|
||||||
|
import Tokenization from './components/tokenization.vue';
|
||||||
|
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
@@ -214,21 +215,18 @@ const filterColumns: TableFilterColumns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function handleTokenization(row: any) {
|
function handleTokenization(row: any) {
|
||||||
dialog.create({
|
const dialogInstance = dialog.create({
|
||||||
title: '代币化产品',
|
title: '代币化产品',
|
||||||
content: '确认将该产品代币化吗?',
|
content: () =>
|
||||||
positiveText: '确认',
|
h(Tokenization, {
|
||||||
negativeText: '取消',
|
data: row,
|
||||||
onPositiveClick: async () => {
|
onClose: () => {
|
||||||
await safeClient(() =>
|
dialogInstance.destroy();
|
||||||
client.api.admin.rwa.tokenization.issue.post({
|
tableInst.value?.reload();
|
||||||
assetCode: row.code,
|
}
|
||||||
productId: row.id,
|
}),
|
||||||
totalSupply: row.estimatedValue
|
style: { width: '600px' },
|
||||||
})
|
showIcon: false
|
||||||
);
|
|
||||||
tableInst.value?.reload();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
src/views/tokenization/product/index.vue
Normal file
70
src/views/tokenization/product/index.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useTemplateRef } from 'vue';
|
||||||
|
import { client, safeClient } from '@/service/api';
|
||||||
|
import type { TableBaseColumns, TableFetchData, TableFilterColumns, TableInst } from '@/components/table';
|
||||||
|
|
||||||
|
const tableInst = useTemplateRef<TableInst>('tableInst');
|
||||||
|
|
||||||
|
const fetchData: TableFetchData = ({ pagination, filter }) => {
|
||||||
|
return safeClient(() =>
|
||||||
|
client.api.admin.rwa.tokenization_schema.configs.get({
|
||||||
|
query: {
|
||||||
|
...pagination,
|
||||||
|
...filter
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: TableBaseColumns = [
|
||||||
|
{
|
||||||
|
key: 'selection',
|
||||||
|
title: '序号',
|
||||||
|
type: 'selection',
|
||||||
|
width: 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '资产代码',
|
||||||
|
key: 'assetCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '发行总量',
|
||||||
|
key: 'totalSupply'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '锁定选项',
|
||||||
|
key: 'lockOptions',
|
||||||
|
render: (row: any) => {
|
||||||
|
return row.lockOptions
|
||||||
|
.map((option: any) => `锁定${option.months}个月,奖励率${(Number(option.rewardRate) * 100).toFixed(2)}%`)
|
||||||
|
.join('; ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关联RWA产品',
|
||||||
|
key: 'productId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'createdAt'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const filterColumns: TableFilterColumns = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TableBase
|
||||||
|
ref="tableInst"
|
||||||
|
:columns="columns"
|
||||||
|
:filter-columns="filterColumns"
|
||||||
|
:fetch-data="fetchData"
|
||||||
|
:header-operations="{
|
||||||
|
add: false,
|
||||||
|
refresh: true,
|
||||||
|
columns: true
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped></style>
|
||||||
Reference in New Issue
Block a user