feat: refactor user list page for new UI

This commit is contained in:
RockYang 2024-03-15 09:29:19 +08:00
parent 960d294aa2
commit 9e2af9dbca
9 changed files with 187 additions and 159 deletions

View File

@ -157,6 +157,7 @@ type ModelAPIConfig struct {
type SystemConfig struct { type SystemConfig struct {
Title string `json:"title"` Title string `json:"title"`
AdminTitle string `json:"admin_title"` AdminTitle string `json:"admin_title"`
Logo string `json:"logo"`
InitPower int `json:"init_power"` // 新用户注册赠送算力值 InitPower int `json:"init_power"` // 新用户注册赠送算力值
RegisterWays []string `json:"register_ways"` // 注册方式:支持手机,邮箱注册 RegisterWays []string `json:"register_ways"` // 注册方式:支持手机,邮箱注册

View File

@ -79,12 +79,12 @@ func (h *ConfigHandler) Get(c *gin.Context) {
return return
} }
var m map[string]interface{} var value map[string]interface{}
err := utils.JsonDecode(config.Config, &m) err := utils.JsonDecode(config.Config, &value)
if err != nil { if err != nil {
resp.ERROR(c, err.Error()) resp.ERROR(c, err.Error())
return return
} }
resp.SUCCESS(c, m) resp.SUCCESS(c, value)
} }

View File

@ -8,7 +8,7 @@ type User struct {
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Salt string `json:"salt"` // 密码盐 Salt string `json:"salt"` // 密码盐
Power int `json:"calls"` // 剩余算力 Power int `json:"power"` // 剩余算力
ChatConfig types.UserChatConfig `json:"chat_config"` // 聊天配置 ChatConfig types.UserChatConfig `json:"chat_config"` // 聊天配置
ChatRoles []string `json:"chat_roles"` // 聊天角色集合 ChatRoles []string `json:"chat_roles"` // 聊天角色集合
ChatModels []string `json:"chat_models"` // AI模型集合 ChatModels []string `json:"chat_models"` // AI模型集合

View File

@ -1,2 +0,0 @@
VITE_PROXY_BASE_URL="/api"
VITE_TARGET_URL="http://172.22.11.2:5678"

View File

@ -1,13 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ChatPlus-Ai</title> <title>极客AI助手-控制台</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { IconDown, IconExport } from "@arco-design/web-vue/es/icon"; import {IconDown, IconExport} from "@arco-design/web-vue/es/icon";
import { useAuthStore } from "@/stores/auth"; import {useAuthStore} from "@/stores/auth";
import useState from "@/composables/useState"; import useState from "@/composables/useState";
import Logo from "/images/logo.png"; import Logo from "/images/logo.png";
import avatar from "/images/user-info.jpg"; import avatar from "/images/user-info.jpg";
@ -8,42 +8,53 @@ import donateImg from "/images/wechat-pay.png";
import SystemMenu from "./SystemMenu.vue"; import SystemMenu from "./SystemMenu.vue";
import PageWrapper from "./PageWrapper.vue"; import PageWrapper from "./PageWrapper.vue";
import {getConfig} from "@/views/System/api";
import {onMounted, ref} from "vue";
const logoWidth = "200px"; const logoWidth = "200px";
const authStore = useAuthStore(); const authStore = useAuthStore();
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const system = ref({})
const reload = async () => {
system.value = (await getConfig({key: "system"})).data;
};
onMounted(async () => {
reload();
});
</script> </script>
<template> <template>
<ALayout class="custom-layout"> <ALayout class="custom-layout">
<ALayoutHeader class="custom-layout-header"> <ALayoutHeader class="custom-layout-header">
<div class="logo"> <div class="logo">
<img :src="Logo" alt="logo" /> <img :src="Logo" alt="logo"/>
<span>ChatPlus 控制台</span> <span>{{ system?.admin_title }}</span>
</div> </div>
<div class="action"> <div class="action">
<ADropdown> <ADropdown>
<ASpace align="center" :size="4"> <ASpace align="center" :size="4">
<a-avatar class="user-avatar" :size="30"> <a-avatar class="user-avatar" :size="30">
<img :src="avatar" /> <img :src="avatar"/>
</a-avatar> </a-avatar>
<IconDown /> <IconDown/>
</ASpace> </ASpace>
<template #content> <template #content>
<a <a
class="dropdown-link" class="dropdown-link"
href="https://github.com/yangjian102621/chatgpt-plus" href="https://github.com/yangjian102621/chatgpt-plus"
target="_blank" target="_blank"
> >
<ADoption value="1"> <ADoption value="1">
<template #icon> <template #icon>
<icon-github /> <icon-github/>
</template> </template>
<span>ChatPlus-AI 创作系统</span> <span>{{ system?.title }}</span>
</ADoption> </ADoption>
</a> </a>
<ADoption value="2" @click="setVisible(true)"> <ADoption value="2" @click="setVisible(true)">
<template #icon> <template #icon>
<icon-wechatpay /> <icon-wechatpay/>
</template> </template>
<span>打赏作者</span> <span>打赏作者</span>
</ADoption> </ADoption>
@ -52,7 +63,7 @@ const [visible, setVisible] = useState(false);
<APopconfirm content="确认退出?" position="bl" @ok="authStore.logout"> <APopconfirm content="确认退出?" position="bl" @ok="authStore.logout">
<AButton status="warning" class="logout-area"> <AButton status="warning" class="logout-area">
<ASpace align="center"> <ASpace align="center">
<IconExport size="16" /> <IconExport size="16"/>
<span>退出登录</span> <span>退出登录</span>
</ASpace> </ASpace>
</AButton> </AButton>
@ -62,26 +73,26 @@ const [visible, setVisible] = useState(false);
</div> </div>
</ALayoutHeader> </ALayoutHeader>
<ALayout> <ALayout>
<SystemMenu :width="logoWidth" /> <SystemMenu :width="logoWidth"/>
<ALayoutContent> <ALayoutContent>
<PageWrapper> <PageWrapper>
<slot /> <slot/>
</PageWrapper> </PageWrapper>
</ALayoutContent> </ALayoutContent>
</ALayout> </ALayout>
</ALayout> </ALayout>
<a-modal <a-modal
v-model:visible="visible" v-model:visible="visible"
class="donate-dialog" class="donate-dialog"
width="400px" width="400px"
title="请作者喝杯咖啡" title="请作者喝杯咖啡"
:footer="false" :footer="false"
> >
<a-alert :closable="false" :show-icon="false"> <a-alert :closable="false" :show-icon="false">
如果你觉得这个项目对你有帮助并且情况允许的话可以请作者喝杯咖啡非常感谢你的支持 如果你觉得这个项目对你有帮助并且情况允许的话可以请作者喝杯咖啡非常感谢你的支持
</a-alert> </a-alert>
<p> <p>
<a-image :src="donateImg" /> <a-image :src="donateImg"/>
</p> </p>
</a-modal> </a-modal>
</template> </template>
@ -90,23 +101,27 @@ const [visible, setVisible] = useState(false);
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
overflow: hidden; overflow: hidden;
&-header { &-header {
display: flex; display: flex;
width: 100%; width: 100%;
height: 60px; height: 60px;
align-items: center; align-items: center;
border-bottom: 1px solid var(--color-neutral-2); border-bottom: 1px solid var(--color-neutral-2);
.logo { .logo {
display: flex; display: flex;
width: v-bind("logoWidth"); width: v-bind("logoWidth");
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 12px; gap: 12px;
img { img {
width: 30px; width: 30px;
height: 30px; height: 30px;
} }
} }
.action { .action {
display: flex; display: flex;
padding: 0 12px; padding: 0 12px;
@ -116,14 +131,17 @@ const [visible, setVisible] = useState(false);
} }
} }
} }
.dropdown-link { .dropdown-link {
text-decoration: none; text-decoration: none;
} }
.donate-dialog { .donate-dialog {
p { p {
text-align: center; text-align: center;
} }
} }
.logout-area { .logout-area {
padding: 8px 0; padding: 8px 0;
display: flex; display: flex;

View File

@ -1,34 +1,34 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from "vue"; import {onMounted} from "vue";
import { Message } from "@arco-design/web-vue"; import {Message} from "@arco-design/web-vue";
import useSubmit from "@/composables/useSubmit"; import useSubmit from "@/composables/useSubmit";
import useRequest from "@/composables/useRequest"; import useRequest from "@/composables/useRequest";
import { getConfig, modelList, save } from "./api"; import {getConfig, modelList, save} from "./api";
import SystemUploader from "./SystemUploader.vue"; import SystemUploader from "./SystemUploader.vue";
const { formRef, formData: system, handleSubmit, submitting } = useSubmit({}); const {formRef, formData: system, handleSubmit, submitting} = useSubmit({});
const [getModelOptions, modelOptions, modelOptionsLoading] = useRequest(modelList); const [getModelOptions, modelOptions, modelOptionsLoading] = useRequest(modelList);
const rules = { const rules = {
title: [{ required: true, message: "请输入网站标题" }], title: [{required: true, message: "请输入网站标题"}],
admin_title: [{ required: true, message: "请输入控制台标题" }], admin_title: [{required: true, message: "请输入控制台标题"}],
}; };
const handleSave = async () => { const handleSave = async () => {
await handleSubmit( await handleSubmit(
() => () =>
save({ save({
key: "system", key: "system",
config: system, config: system,
}), }),
{} {}
); );
Message.success("保存成功"); Message.success("保存成功");
}; };
const reload = async () => { const reload = async () => {
const { data } = await getConfig({ key: "system" }); const {data} = await getConfig({key: "system"});
data && Object.assign(system, data); data && Object.assign(system, data);
}; };
@ -41,43 +41,56 @@ onMounted(async () => {
<a-card :bordered="false"> <a-card :bordered="false">
<a-form ref="formRef" :model="system" :rules="rules" auto-label-width :disabled="submitting"> <a-form ref="formRef" :model="system" :rules="rules" auto-label-width :disabled="submitting">
<a-form-item label="网站标题" field="title"> <a-form-item label="网站标题" field="title">
<a-input v-model="system['title']" /> <a-input v-model="system['title']"/>
</a-form-item> </a-form-item>
<a-form-item label="控制台标题" field="admin_title"> <a-form-item label="控制台标题" field="admin_title">
<a-input v-model="system['admin_title']" /> <a-input v-model="system['admin_title']"/>
</a-form-item> </a-form-item>
<a-form-item label="注册赠送对话次数" field="user_init_calls"> <a-form-item label="网站Logo" field="logo">
<a-input v-model.number="system['init_chat_calls']" placeholder="新用户注册赠送对话次数" /> <SystemUploader v-model="system['logo']" placeholder="推荐图片宽高比为 1:1"/>
</a-form-item> </a-form-item>
<a-form-item label="注册赠送绘图次数" field="init_img_calls"> <a-form-item label="注册赠送算力" field="init_power">
<a-input v-model.number="system['init_img_calls']" placeholder="新用户注册赠送绘图次数" /> <a-input-number v-model="system['init_power']" placeholder="新用户注册赠送初始算力"/>
</a-form-item> </a-form-item>
<a-form-item label="邀请赠送对话次数" field="invite_chat_calls"> <a-form-item label="邀请用户赠送算力" field="invite_power">
<a-input <a-input-number
v-model.number="system['invite_chat_calls']" v-model="system['invite_power']"
placeholder="邀请新用户注册赠送对话次数" placeholder="邀请新用户注册赠送算力"
/> />
</a-form-item> </a-form-item>
<a-form-item label="邀请赠送绘图次数" field="invite_img_calls">
<a-input <a-form-item label="VIP每月赠送算力" field="vip_month_power">
v-model.number="system['invite_img_calls']" <a-input-number v-model="system['vip_month_power']" placeholder="VIP用户每月赠送算力"/>
placeholder="邀请新用户注册赠送绘图次数"
/>
</a-form-item> </a-form-item>
<a-form-item label="VIP每月对话次数" field="vip_month_calls"> <a-form-item label="MJ绘画价格" field="mj_power">
<a-input v-model.number="system['vip_month_calls']" placeholder="VIP用户每月赠送对话次数" /> <a-space>
<a-input-number v-model="system['mj_power']" placeholder=""/>
<a-tooltip content="MidJourney 单次绘图消耗多少单位算力" position="right">
<icon-info-circle-fill size="18"/>
</a-tooltip>
</a-space>
</a-form-item> </a-form-item>
<a-form-item label="VIP每月绘图次数" field="vip_month_img_calls"> <a-form-item label="SD绘画价格" field="sd_power">
<a-input <a-space>
v-model.number="system['vip_month_img_calls']" <a-input-number v-model="system['sd_power']" placeholder=""/>
placeholder="VIP用户每月赠送绘图次数" <a-tooltip content="Stable-Diffusion 单次绘图消耗多少单位算力" position="right">
/> <icon-info-circle-fill size="18"/>
</a-tooltip>
</a-space>
</a-form-item>
<a-form-item label="DALL绘画价格" field="dall_power">
<a-space>
<a-input-number v-model="system['dall_power']" placeholder=""/>
<a-tooltip content="DALL-E-3 单次绘图消耗多少单位算力" position="right">
<icon-info-circle-fill size="18"/>
</a-tooltip>
</a-space>
</a-form-item> </a-form-item>
<a-form-item label="开放注册" field="enabled_register"> <a-form-item label="开放注册" field="enabled_register">
<a-space> <a-space>
<a-switch v-model="system['enabled_register']" /> <a-switch v-model="system['enabled_register']"/>
<a-tooltip content="关闭注册之后只能通过管理后台添加用户" position="right"> <a-tooltip content="关闭注册之后只能通过管理后台添加用户" position="right">
<icon-info-circle-fill size="18" /> <icon-info-circle-fill size="18"/>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</a-form-item> </a-form-item>
@ -89,61 +102,58 @@ onMounted(async () => {
</a-form-item> </a-form-item>
<a-form-item label="启用众筹功能" field="enabled_reward"> <a-form-item label="启用众筹功能" field="enabled_reward">
<a-space> <a-space>
<a-switch v-model="system['enabled_reward']" /> <a-switch v-model="system['enabled_reward']"/>
<a-tooltip content="如果关闭次功能将不在用户菜单显示众筹二维码" position="right"> <a-tooltip content="开启众筹功能允许用户使用个人微信收款码进行收款" position="right">
<icon-info-circle-fill size="18" /> <icon-info-circle-fill size="18"/>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</a-form-item> </a-form-item>
<template v-if="system['enabled_reward']"> <template v-if="system['enabled_reward']">
<a-form-item label="单次对话价格" field="chat_call_price"> <a-form-item label="众筹算力单价" field="power_price">
<a-input v-model="system['chat_call_price']" placeholder="众筹金额跟对话次数的兑换比例" /> <a-input-number v-model="system['power_price']" placeholder="单位算力价格如1块10个单位算力那便填写 0.1"/>
</a-form-item>
<a-form-item label="单次绘图价格" field="img_call_price">
<a-input v-model="system['img_call_price']" placeholder="众筹金额跟绘图次数的兑换比例" />
</a-form-item> </a-form-item>
<a-form-item label="收款二维码" field="reward_img"> <a-form-item label="收款二维码" field="reward_img">
<SystemUploader v-model="system['reward_img']" placeholder="众筹收款二维码地址" /> <SystemUploader v-model="system['reward_img']" placeholder="众筹收款二维码地址"/>
</a-form-item> </a-form-item>
</template> </template>
<a-form-item label="微信客服二维码" field="wechat_card_url"> <a-form-item label="微信客服二维码" field="wechat_card_url">
<SystemUploader v-model="system['wechat_card_url']" placeholder="微信客服二维码" /> <SystemUploader v-model="system['wechat_card_url']" placeholder="微信客服二维码"/>
</a-form-item> </a-form-item>
<a-form-item label="订单超时时间" field="order_pay_timeout"> <a-form-item label="订单超时时间" field="order_pay_timeout">
<a-space style="width: 100%"> <a-space style="width: 100%">
<a-input <a-input-number
v-model.number="system['order_pay_timeout']" v-model="system['order_pay_timeout']"
placeholder="单位:秒" placeholder="单位:秒"
style="width: 100%" style="width: 100%"
/> />
<a-tooltip position="right"> <a-tooltip position="right">
<icon-info-circle-fill size="18" /> <icon-info-circle-fill size="18"/>
<template #content> 系统会定期清理超时未支付的订单<br />默认值900 </template> <template #content> 系统会定期清理超时未支付的订单<br/>默认值900</template>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</a-form-item> </a-form-item>
<a-form-item label="会员充值说明" field="order_pay_info_text"> <a-form-item label="会员充值说明" field="order_pay_info_text">
<a-textarea <a-textarea
v-model="system['order_pay_info_text']" v-model="system['order_pay_info_text']"
:autosize="{ minRows: 3, maxRows: 10 }" :autosize="{ minRows: 3, maxRows: 10 }"
placeholder="请输入会员充值说明文字,比如介绍会员计划" placeholder="请输入会员充值说明文字,比如介绍会员计划"
/> />
</a-form-item> </a-form-item>
<a-form-item label="默认AI模型" field="default_models"> <a-form-item label="默认AI模型" field="default_models">
<a-space style="width: 100%"> <a-space style="width: 100%">
<a-select <a-select
v-model="system['default_models']" v-model="system['default_models']"
multiple multiple
:filterable="true" :filterable="true"
placeholder="选择AI模型多选" placeholder="选择AI模型多选"
:options="modelOptions" :options="modelOptions"
:loading="modelOptionsLoading" :loading="modelOptionsLoading"
:field-names="{ value: 'value', label: 'name' }" :field-names="{ value: 'value', label: 'name' }"
style="width: 100%" style="width: 100%"
> >
</a-select> </a-select>
<a-tooltip content="新用户注册默认开通的 AI 模型" position="right"> <a-tooltip content="新用户注册默认开通的 AI 模型" position="right">
<icon-info-circle-fill size="18" /> <icon-info-circle-fill size="18"/>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</a-form-item> </a-form-item>

View File

@ -1,12 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import SearchTable from "@/components/SearchTable/SearchTable.vue"; import SearchTable from "@/components/SearchTable/SearchTable.vue";
import type { SearchTableColumns } from "@/components/SearchTable/type"; import type {SearchTableColumns} from "@/components/SearchTable/type";
import { getList, save as saveApi, deletApi, resetPassword } from "./api"; import {getList, save as saveApi, deletApi, resetPassword} from "./api";
import UserForm from "./UserForm.vue"; import UserForm from "./UserForm.vue";
import { Message } from "@arco-design/web-vue"; import {Message} from "@arco-design/web-vue";
import { dateFormat } from "@gpt-vue/packages/utils"; import {dateFormat} from "@gpt-vue/packages/utils";
import UserPassword from "./UserPassword.vue"; import UserPassword from "./UserPassword.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup"; import useCustomFormPopup from "@/composables/useCustomFormPopup";
const columns: SearchTableColumns[] = [ const columns: SearchTableColumns[] = [
{ {
title: "账号", title: "账号",
@ -16,21 +17,13 @@ const columns: SearchTableColumns[] = [
}, },
}, },
{ {
title: "剩余对话次数", title: "剩余算力",
dataIndex: "calls", dataIndex: "power",
},
{
title: "剩余绘图次数",
dataIndex: "img_calls",
},
{
title: "累计消耗tokens",
dataIndex: "total_tokens",
}, },
{ {
title: "状态", title: "状态",
dataIndex: "status", dataIndex: "status",
render: ({ record }) => { render: ({record}) => {
return record.status ? "正常" : "停用"; return record.status ? "正常" : "停用";
}, },
}, },
@ -38,15 +31,19 @@ const columns: SearchTableColumns[] = [
title: "过期时间", title: "过期时间",
dataIndex: "expired_time", dataIndex: "expired_time",
width: 180, width: 180,
render: ({ record }) => { render: ({record}) => {
return dateFormat(record.expired_time); if (record.expired_time > 0) {
return dateFormat(record.expired_time)
} else {
return '长期有效'
}
}, },
}, },
{ {
title: "注册时间", title: "注册时间",
dataIndex: "created_at", dataIndex: "created_at",
width: 180, width: 180,
render: ({ record }) => { render: ({record}) => {
return dateFormat(record.created_at); return dateFormat(record.created_at);
}, },
}, },
@ -60,13 +57,13 @@ const columns: SearchTableColumns[] = [
// //
const editModal = useCustomFormPopup(UserForm, saveApi, { const editModal = useCustomFormPopup(UserForm, saveApi, {
popupProps: (arg) => ({ title: arg[0].record ? "编辑用户" : "新增用户" }), popupProps: (arg) => ({title: arg[0].record ? "编辑用户" : "新增用户"}),
}); });
const password = useCustomFormPopup(UserPassword, resetPassword, { const password = useCustomFormPopup(UserPassword, resetPassword, {
popupProps: (arg) => ({ title: "重置密码" }), popupProps: (arg) => ({title: "重置密码"}),
}); });
const handleDelete = async ({ id }: { id: string }, reload) => { const handleDelete = async ({id}: { id: string }, reload) => {
const res = await deletApi(id); const res = await deletApi(id);
if (res.code === 0) { if (res.code === 0) {
Message.success("操作成功"); Message.success("操作成功");
@ -85,7 +82,10 @@ const handleDelete = async ({ id }: { id: string }, reload) => {
</template> </template>
<template #search-extra="{ reload }"> <template #search-extra="{ reload }">
<a-button @click="editModal({ reload })" size="small" type="primary"> <a-button @click="editModal({ reload })" size="small" type="primary">
<template #icon> <icon-plus /> </template>新增 <template #icon>
<icon-plus/>
</template>
新增
</a-button> </a-button>
</template> </template>
</SearchTable> </SearchTable>

View File

@ -1,74 +1,75 @@
<template> <template>
<a-form ref="formRef" :model="form" :style="{ width: '600px' }" @submit="handleSubmit"> <a-form ref="formRef" :model="form" :style="{ width: '600px' }" @submit="handleSubmit">
<a-form-item <a-form-item
field="username" field="username"
label="账号" label="账号"
:rules="[{ required: true, message: '请输入账号' }]" :rules="[{ required: true, message: '请输入账号' }]"
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
> >
<a-input v-model="form.username" placeholder="请输入账号" /> <a-input v-model="form.username" placeholder="请输入账号"/>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
v-if="!props.data.id" v-if="!props.data.id"
field="password" field="password"
label="密码" label="密码"
:rules="[{ required: true, message: '请输入密码' }]" :rules="[{ required: true, message: '请输入密码' }]"
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
showable showable
> >
<a-input v-model="form.password" placeholder="请输入密码" /> <a-input v-model="form.password" placeholder="请输入密码"/>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="calls" field="calls"
label="对话次数" label="对话次数"
:rules="[{ required: true, message: '请输入对话次数' }]" :rules="[{ required: true, message: '请输入对话次数' }]"
> >
<a-input-number v-model="form.calls" placeholder="请输入对话次数" /> <a-input-number v-model="form.calls" placeholder="请输入对话次数"/>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="img_calls" field="img_calls"
label="绘图次数" label="绘图次数"
:rules="[{ required: true, message: '请输入绘图次数' }]" :rules="[{ required: true, message: '请输入绘图次数' }]"
> >
<a-input-number v-model="form.img_calls" placeholder="请输入绘图次数" /> <a-input-number v-model="form.img_calls" placeholder="请输入绘图次数"/>
</a-form-item> </a-form-item>
<a-form-item field="expired_time" label="有效期"> <a-form-item field="expired_time" label="有效期">
<a-date-picker v-model="form.expired_time" placeholder="请选择有效期" /> <a-date-picker v-model="form.expired_time" placeholder="请选择有效期"/>
</a-form-item> </a-form-item>
<a-form-item field="chat_roles" label="聊天角色"> <a-form-item field="chat_roles" label="聊天角色">
<a-select <a-select
:field-names="{ value: 'key', label: 'name' }" :field-names="{ value: 'key', label: 'name' }"
v-model="form.chat_roles" v-model="form.chat_roles"
placeholder="请选择聊天角色" placeholder="请选择聊天角色"
multiple multiple
:options="roleOption" :options="roleOption"
:rules="[{ required: true, message: '请选择聊天角色' }]" :rules="[{ required: true, message: '请选择聊天角色' }]"
> >
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item field="chat_models" label="模型角色"> <a-form-item field="chat_models" label="模型角色">
<a-select <a-select
:field-names="{ value: 'value', label: 'name' }" :field-names="{ value: 'value', label: 'name' }"
v-model="form.chat_models" v-model="form.chat_models"
placeholder="请选择模型角色" placeholder="请选择模型角色"
multiple multiple
:options="modalOption" :options="modalOption"
:rules="[{ required: true, message: '请选择模型角色' }]" :rules="[{ required: true, message: '请选择模型角色' }]"
> >
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item field="status" label="启用状态"> <a-form-item field="status" label="启用状态">
<a-switch v-model="form.status" /> <a-switch v-model="form.status"/>
</a-form-item> </a-form-item>
<a-form-item field="vip" label="开通VIP"> <a-form-item field="vip" label="开通VIP">
<a-switch v-model="form.vip" /> <a-switch v-model="form.vip"/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</template> </template>
<script setup> <script setup>
import { ref, defineExpose, defineProps } from "vue"; import {ref, defineExpose, defineProps} from "vue";
import { getModel, getRole } from "./api"; import {getModel, getRole} from "./api";
const props = defineProps({ const props = defineProps({
data: {}, data: {},
}); });
@ -96,7 +97,7 @@ if (props.data?.id) {
const modalOption = ref([]); const modalOption = ref([]);
const roleOption = ref([]); const roleOption = ref([]);
const getOption = (api, container) => { const getOption = (api, container) => {
api().then(({ code, data }) => { api().then(({code, data}) => {
if (code === 0) { if (code === 0) {
container.value = data; container.value = data;
} }