mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-20 18:26:37 +08:00
feat: refactor user list page for new UI
This commit is contained in:
parent
960d294aa2
commit
9e2af9dbca
@ -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"` // 注册方式:支持手机,邮箱注册
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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模型集合
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
VITE_PROXY_BASE_URL="/api"
|
|
||||||
VITE_TARGET_URL="http://172.22.11.2:5678"
|
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user