Compare commits

...

4 Commits

Author SHA1 Message Date
李寻欢 95a85e2128 结构调整 2024-02-05 15:19:56 +08:00
李寻欢 82f9a8d529 优化左上角网站标题显示效果 2024-02-05 14:56:41 +08:00
李寻欢 1084399033 完成机器人页面及相关功能 2024-02-05 14:56:22 +08:00
李寻欢 3624842e22 优化移动端显示效果 2024-02-05 11:01:16 +08:00
10 changed files with 414 additions and 8 deletions

49
src/api/robot.ts Normal file
View File

@ -0,0 +1,49 @@
import { http } from "@/utils/http";
import { BaseResponse } from "@/api/base";
/** 机器人信息 */
export type RobotInfo = {
id: string;
createdAt: string;
updatedAt: string;
wxid: string;
account: string;
name: string;
avatar: string;
mobile: string;
currentDataPath: string;
dataSavePath: string;
dbKey: string;
hookApi: string;
remark: string;
version: number;
vncUrl: string;
tag: string;
/* 页面使用参数,后端不返回 */
saveLoading: boolean;
edit: boolean;
};
/** 获取所有机器人 */
export const getAllRobot = (data: any) => {
return http.request<BaseResponse<Array<RobotInfo>>>(
"get",
"/admin/v1/robot",
{ data }
);
};
/* 保存机器人 */
export const saveRobot = (data: any) => {
return http.request<BaseResponse<RobotInfo>>("post", "/admin/v1/robot", {
data
});
};
/* 删除机器人信息 */
export const deleteRobot = (id: string) => {
return http.request<BaseResponse<RobotInfo>>(
"delete",
`/admin/v1/robot/${id}`
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

View File

@ -2,13 +2,13 @@
font-family: "阿里巴巴普惠体";
font-style: normal;
font-weight: 300;
src: url("./Alibaba_PuHuiTi_2.0_55_Regular_55_Regular.ttf");
src: url("https://minio.ltd/fonts/Alibaba_PuHuiTi_2.0_55_Regular_55_Regular.ttf");
}
@font-face {
font-family: "哥特黑白无常";
font-style: normal;
font-weight: 300;
src: url("./gthbwc.ttf");
src: url("https://minio.ltd/fonts/gthbwc.ttf");
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -55,11 +55,12 @@ const { title } = useNav();
}
.sidebar-title {
font-family: "哥特黑白无常", system-ui !important;
display: inline-block;
height: 32px;
height: 28px;
margin: 2px 0 0 12px;
overflow: hidden;
font-size: 18px;
font-size: 20px;
font-weight: 600;
line-height: 32px;
color: $subMenuActiveText;

View File

@ -2,7 +2,7 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types";
import userAvatar from "@/assets/user.jpg";
import userAvatar from "@/assets/avatar/defaultWeChatAvatar.gif";
import { getTopMenu } from "@/router/utils";
import { useGlobal } from "@pureadmin/utils";
import { useRouter, useRoute } from "vue-router";

View File

@ -275,7 +275,7 @@ onMounted(() => {
.assistant-card {
width: 32%;
min-width: 330px;
min-width: 380px;
height: 330px;
}

View File

@ -1,12 +1,368 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import { getAllRobot, RobotInfo, saveRobot, deleteRobot } from "@/api/robot";
import { message } from "@/utils/message";
import defaultWeChatAvatar from "@/assets/avatar/defaultWeChatAvatar.gif";
defineOptions({
name: "RobotList"
});
//
const showCreateDialog = ref(false);
//
const showVncDialog = ref(false);
//
const dataLoading = ref(false);
// 使
const versionList = ref([
{ name: "v3.9.2.23", value: 39223 },
{ name: "v3.9.5.81", value: 39581 },
{ name: "v3.9.8.25", value: 39825 }
]);
//
const robotList = ref([] as RobotInfo[]);
//
const vncUrl = ref("");
//
const translateVersion = (value: number) => {
return versionList.value.find(item => item.value === value)?.name;
};
//
const createRobotParam = reactive({
id: "",
hookApi: "",
version: 39581,
vncUrl: "",
remark: "",
tag: "",
saveLoading: false
});
//
const clearCreateParam = (done: () => void) => {
createRobotParam.hookApi = "";
createRobotParam.version = 39581;
createRobotParam.vncUrl = "";
createRobotParam.remark = "";
createRobotParam.tag = "";
done();
};
//
const closeVncIframe = (done: () => void) => {
showVncDialog.value = false;
vncUrl.value = "";
done();
};
//
const showVncDialogHandle = (url: string) => {
vncUrl.value = url;
showVncDialog.value = true;
};
//
const getAllRobotHandle = async () => {
//
dataLoading.value = true;
//
getAllRobot({ keyword: "", tag: "" })
.then(res => {
//
robotList.value = res.data;
})
.catch(() => {
message("获取机器人列表失败", { type: "error" });
})
.finally(() => {
//
dataLoading.value = false;
});
};
//
const saveRobotHandle = async (item: any, isCreate?: boolean) => {
//
item.saveLoading = true;
//
const param = JSON.parse(JSON.stringify(item));
// hookApivnc
if (param.hookApi !== "" && !param.hookApi.startsWith("http://")) {
param.hookApi = `http://${item.hookApi}`;
}
if (param.vncUrl !== "" && !param.vncUrl.startsWith("http://")) {
param.vncUrl = `http://${param.vncUrl}/vnc_lite.html`;
}
//
saveRobot(param)
.then(() => {
message("保存成功", { type: "success" });
if (isCreate) {
showCreateDialog.value = false;
}
//
getAllRobotHandle();
})
.catch(e => {
const response = e.response.data;
message(
response.message + (response.errMsg ? ": " + response.errMsg : ""),
{ type: "error" }
);
})
.finally(() => {
//
item.saveLoading = false;
});
};
//
const deleteRobotHandle = (id: string) => {
deleteRobot(id)
.then(() => {
message("删除成功", { type: "success" });
getAllRobotHandle();
})
.catch(e => {
const response = e.response.data;
message(
response.message + (response.errMsg ? ": " + response.errMsg : ""),
{ type: "error" }
);
});
};
onMounted(() => {
//
getAllRobotHandle();
});
</script>
<template>
<div class="flex justify-center items-center h-[640px]">
<h1>机器人列表</h1>
<div>
<div style="margin: 20px">
<el-button type="primary" @click="showCreateDialog = true"
>新增机器人</el-button
>
</div>
<div class="flex h-[640px]">
<div class="card-container">
<el-card
class="robot-card"
shadow="hover"
v-for="item in robotList"
:key="item.id"
v-loading="item.saveLoading"
>
<template #header>
<div class="robot-header">
<div style="display: flex; align-items: center">
<el-avatar
:size="50"
:src="item.avatar ? item.avatar : defaultWeChatAvatar"
/>
<span style="margin-left: 10px">
<p class="robot-name">{{ item.name }}</p>
<p v-if="!item.edit" class="robot-remark">
{{ item.remark }}
</p>
<el-input
v-else
v-model="item.remark"
style="width: 80%"
size="small"
/>
</span>
</div>
<el-button-group>
<el-button
link
:type="item.edit ? 'primary' : 'warning'"
@click="item.edit = !item.edit"
>
{{ item.edit ? "取消" : "编辑" }}
</el-button>
<el-popconfirm
v-if="!item.edit"
title="是否删除当前机器人?"
confirm-button-text="是"
cancel-button-text="否"
@confirm="deleteRobotHandle(item.id)"
>
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
<el-button
v-else
link
type="success"
@click="saveRobotHandle(item)"
>保存</el-button
>
</el-button-group>
</div>
</template>
<el-descriptions :column="1" size="default">
<el-descriptions-item label="微信Id">
{{ item.wxid }}
</el-descriptions-item>
<el-descriptions-item label="手机号">
{{ item.mobile }}
</el-descriptions-item>
<el-descriptions-item label="HOOK地址">
{{ item.hookApi }}
</el-descriptions-item>
<el-descriptions-item label="VNC地址">
<el-button
link
type="primary"
@click="showVncDialogHandle(item.vncUrl)"
>
{{ item.vncUrl }}
</el-button>
</el-descriptions-item>
<el-descriptions-item label="微信版本">
{{ translateVersion(item.version) }}
</el-descriptions-item>
<el-descriptions-item label="标签">
<el-tag
:key="t"
v-show="item.tag != ''"
v-for="t in item.tag.split(',')"
type="success"
>
{{ t }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
</div>
<el-dialog
v-model="showCreateDialog"
title="新增机器人"
width="40%"
:close-on-click-modal="false"
:before-close="clearCreateParam"
>
<el-form :model="createRobotParam" label-width="100px">
<el-form-item
label="HOOK地址"
prop="hookApi"
:rules="[
{ required: true, message: '请输入HOOK地址', trigger: 'blur' }
]"
>
<el-input
v-model="createRobotParam.hookApi"
placeholder="请输入Hook接口地址格式: 10.0.0.71:19088"
>
<template #prepend>http://</template>
</el-input>
</el-form-item>
<el-form-item label="微信版本" prop="version">
<el-select
v-model="createRobotParam.version"
placeholder="请选择微信版本"
:rules="[
{ required: true, message: '请选择微信版本', trigger: 'blur' }
]"
>
<el-option
v-for="item in versionList"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="VNC地址">
<el-input
v-model="createRobotParam.vncUrl"
placeholder="请输入VNC页面地址格式: 10.0.0.71:19087"
>
<template #prepend>http://</template>
<template #append>/vnc_lite.html</template>
</el-input>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="createRobotParam.remark"
placeholder="请输入备注"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button
type="primary"
v-loading="createRobotParam.saveLoading"
@click="saveRobotHandle(createRobotParam, true)"
>
保存
</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="showVncDialog"
width="1280px"
:close-on-click-modal="false"
:before-close="closeVncIframe"
center
>
<div style="margin: 0 auto">
<iframe :src="vncUrl" class="vncIframe" />
</div>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
.card-container {
margin: 10px;
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: flex-start; /* 改为 flex-start 以优先横向排列 */
}
.robot-card {
width: 24%;
min-width: 380px;
height: 330px;
}
.robot-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.robot-name {
font-size: 18px;
font-weight: bold;
}
.robot-remark {
font-size: 14px;
color: #666;
}
.vncIframe {
width: 1280px;
height: 747px;
transform-origin: left top;
transform: scale(0.95, 0.95);
}
</style>