重构主体工作完成

This commit is contained in:
RockYang
2024-03-15 18:35:10 +08:00
61 changed files with 1043 additions and 1097 deletions

View File

@@ -1,7 +0,0 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint"
]
}

View File

@@ -0,0 +1,49 @@
<script lang="ts" setup>
import { computed } from "vue";
import { Message } from "@arco-design/web-vue";
import type { UploadInstance, FileItem } from "@arco-design/web-vue";
import { uploadUrl } from "@/http/config";
defineProps({
modelValue: String,
placeholder: String,
});
const emits = defineEmits(["update:modelValue"]);
const uploadProps = computed<UploadInstance["$props"]>(() => {
const TOKEN = JSON.parse(localStorage.getItem(__AUTH_KEY))?.token;
return {
accept: "image/*",
action: uploadUrl,
name: "file",
headers: { [__AUTH_KEY]: TOKEN },
showFileList: false,
};
});
const handleChange = (_, file: FileItem) => {
if (file?.response) {
emits("update:modelValue", file?.response?.data?.url);
Message.success("上传成功");
}
};
</script>
<template>
<a-upload v-bind="uploadProps" style="width: 100%" @change="handleChange">
<template #upload-button>
<a-input-group style="width: 100%">
<a-input
:model-value="modelValue"
:placeholder="placeholder"
readonly
allow-clear
@clear.stop="emits('update:modelValue')"
/>
<a-button type="primary" style="width: 100px">
<icon-cloud />
</a-button>
</a-input-group>
</template>
</a-upload>
</template>

View File

@@ -1,18 +1,17 @@
<script lang="ts" setup>
import { computed, ref, onActivated } from "vue";
import { computed, onActivated } from "vue";
import useAsyncTable from "./useAsyncTable";
import { useTableScroll } from "@/components/SearchTable/utils";
import { Message } from "@arco-design/web-vue";
import { Message, type TableColumnData } from "@arco-design/web-vue";
import type { TableRequest, TableOriginalProps } from "./useAsyncTable";
interface SimpleTable extends /* @vue-ignore */ TableOriginalProps {
request: TableRequest<Record<string, unknown>>;
params?: Record<string, unknown>;
columns?: TableOriginalProps["columns"];
columns?: TableColumnData[];
}
const props = defineProps<SimpleTable>();
const tableContainerRef = ref<HTMLElement>();
// 表格请求参数
const [tableConfig, getList] = useAsyncTable(props.request, props.params);
@@ -45,7 +44,7 @@ onActivated(handleSearch);
...$attrs,
...tableConfig,
...props,
scroll: useTableScroll(_columns || [], tableContainerRef as HTMLElement),
scroll: useTableScroll(_columns || []),
columns: _columns,
}"
>

View File

@@ -2,11 +2,11 @@
import { getList, save, deleting, setStatus } from "./api";
import ApiKeyForm from "./ApiKeyForm.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup";
import { Message } from "@arco-design/web-vue";
import { Message, type TableColumnData } from "@arco-design/web-vue";
import SimpleTable from "@/components/SimpleTable/SimpleTable.vue";
import { dateFormat } from "@chatgpt-plus/packages/utils";
// table 配置
const columns = [
const columns: TableColumnData[] = [
{
title: "所属平台",
dataIndex: "platform",
@@ -20,6 +20,11 @@ const columns = [
dataIndex: "value",
slotName: "value",
},
{
title: "API URL",
dataIndex: "api_url",
slotName: "value",
},
{
title: "用途",
dataIndex: "type",
@@ -85,17 +90,26 @@ const handleStatusChange = ({ filed, value, record, reload }) => {
</script>
<template>
<SimpleTable :columns="columns" :request="getList">
<template #header="{ reload }">
<a-space>
<a-button @click="popup({ reload })" size="small" type="primary"
><template #icon> <icon-plus /> </template>新增
</a-button>
<a-button type="primary" status="success" href="https://gpt.bemore.lol" target="_blank">
<template #icon>
<icon-link />
</template>
购买API-KEY
</a-button>
</a-space>
</template>
<template #action="{ record, reload }">
<a-link @click="popup({ record, reload })">编辑</a-link>
<a-popconfirm content="确定删除?" @ok="handleDelete(record, reload)">
<a-link status="danger">删除</a-link>
</a-popconfirm>
</template>
<template #header="{ reload }">
<a-button @click="popup({ reload })" size="small" type="primary"
><template #icon> <icon-plus /> </template>新增
</a-button>
</template>
<template #value="{ record, column }">
<a-typography-text copyable ellipsis style="margin: 0">
{{ record[column.dataIndex] }}

View File

@@ -1,9 +1,8 @@
<template>
<a-alert type="warning">
<div class="warning">
{{
`注意如果是百度文心一言平台API-KEY 为 APIKey|SecretKey中间用竖线|)连接\n注意如果是讯飞星火大模型API-KEY 为 AppId|APIKey|APISecret,中间用竖线(|)连接`
}}
<div>注意如果是百度文心一言平台API-KEY APIKey|SecretKey中间用竖线|连接</div>
<div>注意如果是讯飞星火大模型API-KEY AppId|APIKey|APISecret中间用竖线|连接</div>
</div>
</a-alert>
<a-form
@@ -18,7 +17,12 @@
:rules="[{ required: true, message: '请输入所属平台' }]"
:validate-trigger="['change', 'input']"
>
<a-input v-model="form.platform" placeholder="请输入所属平台" />
<a-select
v-model="form.platform"
placeholder="请输入所属平台"
:options="platformOptions"
@change="handlePlatformChange"
/>
</a-form-item>
<a-form-item
field="name"
@@ -35,7 +39,12 @@
:rules="[{ required: true, message: '请输入用途' }]"
:validate-trigger="['change', 'input']"
>
<a-select v-model="form.type" placeholder="请输入用途" :options="typeOPtions"> </a-select>
<a-select
v-model="form.type"
placeholder="请输入用途"
:options="typeOptions"
@change="handlePlatformChange"
/>
</a-form-item>
<a-form-item
field="value"
@@ -55,13 +64,15 @@
</a-form-item>
<a-form-item field="use_proxy" label="使用代理">
<a-switch v-model="form.use_proxy" />
<a-tooltip
content="是否使用代理访问 API URLOpenAI 官方API需要开启代理访问"
position="right"
>
<icon-info-circle-fill />
</a-tooltip>
<a-space>
<a-switch v-model="form.use_proxy" />
<a-tooltip
content="是否使用代理访问 API URLOpenAI 官方API需要开启代理访问"
position="right"
>
<icon-info-circle-fill />
</a-tooltip>
</a-space>
</a-form-item>
<a-form-item field="enable" label="启用状态">
<a-switch v-model="form.enable" />
@@ -86,7 +97,7 @@ defineExpose({
form,
});
const typeOPtions = [
const typeOptions = [
{
label: "聊天",
value: "chart",
@@ -96,6 +107,48 @@ const typeOPtions = [
value: "img",
},
];
const platformOptions = [
{
label: "【OpenAI】ChatGPT",
value: "OpenAI",
api_url: "https://gpt.bemore.lol/v1/chat/completions",
img_url: "https://gpt.bemore.lol/v1/images/generations",
},
{
label: "【讯飞】星火大模型",
value: "XunFei",
api_url: "wss://spark-api.xf-yun.com/{version}/chat",
},
{
label: "【清华智普】ChatGLM",
value: "ChatGLM",
api_url: "https://open.bigmodel.cn/api/paas/v3/model-api/{model}/sse-invoke",
},
{
label: "【百度】文心一言",
value: "Baidu",
api_url: "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/{model}",
},
{
label: "【微软】Azure",
value: "Azure",
api_url:
"https://chat-bot-api.openai.azure.com/openai/deployments/{model}/chat/completions?api-version=2023-05-15",
},
{
label: "【阿里】千义通问",
value: "QWen",
api_url: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
},
];
const handlePlatformChange = () => {
const obj = platformOptions.find((item) => item.value === form.value.platform);
if (obj) {
form.value.api_url = form.value.type === "img" ? obj.img_url : obj.api_url;
}
};
</script>
<style lang="less" scoped>
.content-title {

View File

@@ -1,13 +1,12 @@
<script lang="ts" setup>
import { getList, save, deleting, setStatus } from "./api";
import { ref } from "vue";
import ChatModelForm from "./ChatModelForm.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup";
import { Message } from "@arco-design/web-vue";
import { Message, type TableColumnData } from "@arco-design/web-vue";
import SimpleTable from "@/components/SimpleTable/SimpleTable.vue";
import { dateFormat } from "@chatgpt-plus/packages/utils";
// table 配置
const columns = [
const columns: TableColumnData[] = [
{
title: "所属平台",
dataIndex: "platform",
@@ -49,20 +48,9 @@ const columns = [
},
];
// 数据
const tableData = ref([]);
const getData = () => {
getList().then(({ code, data }) => {
if (code === 0) {
tableData.value = data;
}
});
};
getData();
// 新增编辑
const popup = useCustomFormPopup(ChatModelForm, save, {
popupProps: (arg) => ({ title: arg[0].record ? "编辑ApiKey" : "新增ApiKey" }),
popupProps: (arg) => ({ title: arg[0].record ? "编辑模型" : "新增模型" }),
});
// 删除

View File

@@ -6,7 +6,7 @@
:rules="[{ required: true, message: '请输入所属平台' }]"
:validate-trigger="['change', 'input']"
>
<a-input v-model="form.platform" placeholder="请输入所属平台" />
<a-select v-model="form.platform" placeholder="请输入所属平台" :options="platformOptions" />
</a-form-item>
<a-form-item
field="name"
@@ -33,10 +33,12 @@
:validate-trigger="['change', 'input']"
showable
>
<a-input-number v-model="form.weight" placeholder="请输入对话权重" />
<a-tooltip content="对话权重,每次对话扣减多少次对话额度" position="right">
<icon-info-circle-fill />
</a-tooltip>
<a-space>
<a-input-number v-model="form.weight" placeholder="请输入对话权重" />
<a-tooltip content="对话权重,每次对话扣减多少次对话额度" position="right">
<icon-info-circle-fill />
</a-tooltip>
</a-space>
</a-form-item>
<a-form-item field="open" label="开放状态代理">
@@ -65,15 +67,13 @@ defineExpose({
form,
});
const typeOPtions = [
{
label: "聊天",
value: "chart",
},
{
label: "绘图",
value: "img",
},
const platformOptions = [
{ label: "【OpenAI】ChatGPT", value: "OpenAI" },
{ label: "【讯飞】星火大模型", value: "XunFei" },
{ label: "【清华智普】ChatGLM", value: "ChatGLM" },
{ label: "【百度】文心一言", value: "Baidu" },
{ label: "【微软】Azure", value: "Azure" },
{ label: "【阿里】通义千问", value: "QWen" },
];
</script>
<style lang="less" scoped>

View File

@@ -8,7 +8,7 @@ import app from "@/main";
import { getList, message, remove } from "./api";
import ChatsLogs from "./ChatsLogs.vue";
const columns: SearchTableColumns[] = [
const chatColumns: SearchTableColumns[] = [
{
dataIndex: "user_id",
title: "账户ID",
@@ -42,10 +42,48 @@ const columns: SearchTableColumns[] = [
dataIndex: "token",
title: "消耗算力",
},
{
dataIndex: "created_at",
title: "创建时间",
search: {
valueType: "range",
},
render: ({ record }) => dateFormat(record.created_at),
},
{
title: "操作",
fixed: "right",
slotName: "actions",
},
];
const messageColumns: SearchTableColumns[] = [
{
dataIndex: "user_id",
title: "账户ID",
search: {
valueType: "input",
},
},
{
dataIndex: "username",
title: "账户",
},
{
dataIndex: "title",
title: "标题",
search: {
valueType: "input",
},
},
{
dataIndex: "msg_num",
title: "消息数量",
},
{
dataIndex: "token",
title: "消耗算力",
},
{
dataIndex: "created_at",
title: "创建时间",
@@ -62,8 +100,8 @@ const columns: SearchTableColumns[] = [
];
const tabsList = [
{ key: "1", title: "对话列表", api: getList, columns },
{ key: "2", title: "消息记录", api: message, columns },
{ key: "1", title: "对话列表", api: getList, columns: chatColumns },
{ key: "2", title: "消息记录", api: message, columns: messageColumns },
];
const activeKey = ref(tabsList[0].key);
@@ -101,7 +139,7 @@ const handleCheck = (record) => {
content="是否删除?"
position="left"
type="warning"
:on-before-ok="() => handleRemove(record.id, reload)"
:on-before-ok="() => handleRemove(record.chat_id, reload)"
>
<a-link status="danger">删除</a-link>
</a-popconfirm>

View File

@@ -8,10 +8,10 @@ export const getList = (data?: Record<string, unknown>) => {
})
}
export const remove = (data) => {
export const remove = (params) => {
return http({
url: "/api/admin/order/remove",
method: "post",
data
method: "get",
params
})
}

View File

@@ -1,13 +1,12 @@
<script lang="ts" setup>
import { getList, save, deleting, setStatus } from "./api";
import { ref } from "vue";
import ProductForm from "./ProductForm.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup";
import { Message } from "@arco-design/web-vue";
import { Message, type TableColumnData } from "@arco-design/web-vue";
import SimpleTable from "@/components/SimpleTable/SimpleTable.vue";
import { dateFormat } from "@chatgpt-plus/packages/utils";
// table 配置
const columns = [
const columns: TableColumnData[] = [
{
title: "产品名称",
dataIndex: "name",
@@ -59,17 +58,6 @@ const columns = [
},
];
// 数据
const tableData = ref([]);
const getData = () => {
getList().then(({ code, data }) => {
if (code === 0) {
tableData.value = data;
}
});
};
getData();
// 新增编辑
const popup = useCustomFormPopup(ProductForm, save, {
popupProps: (arg) => ({ title: arg[0].record ? "编辑产品" : "新增产品" }),

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { getList, save, deleting, setStatus } from "./api";
import { reactive, ref } from "vue";
import { reactive } from "vue";
import RoleForm from "./RoleForm.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup";
import { Message } from "@arco-design/web-vue";
@@ -40,17 +40,6 @@ const expandable = reactive({
width: 50,
});
// 数据
const tableData = ref([]);
const getData = () => {
getList().then(({ code, data }) => {
if (code === 0) {
tableData.value = data;
}
});
};
getData();
//展开行table
const expandColumns = [
{

View File

@@ -23,7 +23,7 @@
:rules="[{ required: true, message: '请输入角色图标' }]"
:validate-trigger="['change', 'input']"
>
<a-input v-model="form.icon" placeholder="请输入角色图标" />
<CustomUploader v-model="form.icon" placeholder="请输入角色图标" />
</a-form-item>
<a-form-item
field="hello_msg"
@@ -67,6 +67,7 @@
<script setup>
import { ref, defineExpose, defineProps } from "vue";
import CustomUploader from "@/components/CustomUploader.vue";
const props = defineProps({
data: {},
});

View File

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

View File

@@ -8,6 +8,11 @@ import { dateFormat } from "@chatgpt-plus/packages/utils";
import UserPassword from "./UserPassword.vue";
import useCustomFormPopup from "@/composables/useCustomFormPopup";
const columns: SearchTableColumns[] = [
{
title: "用户头像",
dataIndex: "avatar",
slotName: "avatar",
},
{
title: "账号",
dataIndex: "username",
@@ -16,17 +21,9 @@ const columns: SearchTableColumns[] = [
},
},
{
title: "剩余对话次数",
title: "剩余算力",
dataIndex: "calls",
},
{
title: "剩余绘图次数",
dataIndex: "img_calls",
},
{
title: "累计消耗tokens",
dataIndex: "total_tokens",
},
{
title: "状态",
dataIndex: "status",
@@ -38,9 +35,7 @@ const columns: SearchTableColumns[] = [
title: "过期时间",
dataIndex: "expired_time",
width: 180,
render: ({ record }) => {
return dateFormat(record.expired_time);
},
slotName: "expired_time",
},
{
title: "注册时间",
@@ -76,6 +71,15 @@ const handleDelete = async ({ id }: { id: string }, reload) => {
</script>
<template>
<SearchTable :request="getList" :columns="columns">
<template #avatar="{ record }">
<a-avatar>
<a-image :src="record.avatar" style="border-radius: 50%" />
</a-avatar>
</template>
<template #expired_time="{ record }">
<a-tag v-if="record.expired_time === 0" color="blue">长期有效</a-tag>
<template v-else>{{ dateFormat(record.expired_time) }}</template>
</template>
<template #actions="{ record, reload }">
<a-link @click="editModal({ record, reload })">编辑</a-link>
<a-popconfirm content="确定删除?" @ok="handleDelete(record, reload)">

View File

@@ -20,17 +20,10 @@
</a-form-item>
<a-form-item
field="calls"
label="对话次数"
:rules="[{ required: true, message: '请输入对话次数' }]"
label="用户算力"
:rules="[{ required: true, message: '请输入用户算力' }]"
>
<a-input-number v-model="form.calls" placeholder="请输入对话次数" />
</a-form-item>
<a-form-item
field="img_calls"
label="绘图次数"
:rules="[{ required: true, message: '请输入绘图次数' }]"
>
<a-input-number v-model="form.img_calls" placeholder="请输入绘图次数" />
<a-input-number v-model="form.calls" placeholder="请输入用户算力" />
</a-form-item>
<a-form-item field="expired_time" label="有效期">
<a-date-picker v-model="form.expired_time" placeholder="请选择有效期" />

View File

@@ -2,137 +2,115 @@ html,
body,
#app,
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
width: 100%;
height: 100%;
overflow: hidden;
}
body {
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
.admin-home a {
text-decoration: none;
text-decoration: none;
}
.admin-home .content-box {
position: absolute;
left: 250px;
right: 0;
top: 0;
bottom: 0;
/*padding-bottom: 30px;*/
-webkit-transition: left 0.3s ease-in-out;
transition: left 0.3s ease-in-out;
background: #f0f0f0;
overflow-y: scroll;
position: absolute;
left: 250px;
right: 0;
top: 0;
bottom: 0;
padding-bottom: 30px;
-webkit-transition: left 0.3s ease-in-out;
transition: left 0.3s ease-in-out;
background: #f0f0f0;
}
.admin-home .content-box .content {
width: auto;
padding: 10px;
box-sizing: border-box;
/*BaseForm*/
width: auto;
height: 100%;
padding: 10px;
overflow-y: scroll;
box-sizing: border-box;
/*BaseForm*/
}
.admin-home .content-box .content .container {
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}
.admin-home .content-box .content .container .handle-box {
margin-bottom: 20px;
margin-bottom: 20px;
}
.admin-home .content-box .content .crumbs {
margin: 10px 0;
margin: 10px 0;
}
.admin-home .content-box .content .el-table th {
background-color: #f5f7fa !important;
background-color: #f5f7fa !important;
}
.admin-home .content-box .content .pagination {
margin: 20px 0;
display: flex;
justify-content: center;
width: 100%;
margin: 20px 0;
display: flex;
justify-content: center;
width: 100%;
}
.admin-home .content-box .content .plugins-tips {
padding: 20px 10px;
margin-bottom: 20px;
padding: 20px 10px;
margin-bottom: 20px;
}
.admin-home .content-box .content .el-button + .el-tooltip {
margin-left: 10px;
margin-left: 10px;
}
.admin-home .content-box .content .el-table tr:hover {
background: #f6faff;
background: #f6faff;
}
.admin-home .content-box .content .mgb20 {
margin-bottom: 20px;
margin-bottom: 20px;
}
.admin-home .content-box .content .move-enter-active,
.admin-home .content-box .content .move-leave-active {
transition: opacity 0.1s ease;
transition: opacity 0.1s ease;
}
.admin-home .content-box .content .move-enter-from,
.admin-home .content-box .content .move-leave-to {
opacity: 0;
opacity: 0;
}
.admin-home .content-box .content .form-box {
width: 600px;
width: 600px;
}
.admin-home .content-box .content .form-box .line {
text-align: center;
text-align: center;
}
.admin-home .content-box .content .el-time-panel__content::after,
.admin-home .content-box .content .el-time-panel__content::before {
margin-top: -7px;
margin-top: -7px;
}
.admin-home .content-box .content .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
padding-bottom: 0;
padding-bottom: 0;
}
.admin-home .content-box .content [class*=" el-icon-"],
.admin-home .content-box .content [class^=el-icon-] {
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.admin-home .content-box .content .el-sub-menu [class^=el-icon-] {
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
vertical-align: middle;
margin-right: 5px;
width: 24px;
text-align: center;
font-size: 18px;
}
.admin-home .content-box .content [hidden] {
display: none !important;
display: none !important;
}
.admin-home .content-collapse {
left: 65px;
left: 65px;
}